Merge pull request #894 from steveseguin/19.1

Version 19.1
This commit is contained in:
Steve Seguin 2021-09-19 00:32:59 -04:00 committed by GitHub
commit 2d61fd2ff0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
141 changed files with 26855 additions and 3440 deletions

404
blank.json Normal file
View File

@ -0,0 +1,404 @@
{
"titles": {
"join-by-room-name-here": "Enter a room name to quick join",
"join-room": "Join room",
"load-the-next-guest-in-queue": "Load the next guest in queue",
"toggle-the-chat": "Toggle the Chat",
"mute-the-speaker": "Mute the Speaker",
"mute-the-mic": "Mute the Mic",
"disable-the-camera": "Disable the Camera",
"share-a-screen-with-others": "Share a Screen with others",
"create-a-secondary-stream": "Create a Secondary Stream",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"hangup-the-call": "Hangup the Call",
"alert-the-host-you-want-to-speak": "Alert the host you want to speak",
"record-your-stream-to-disk": "Record your stream to disk",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"cancel-the-director-s-video-audio": "Cancel the Director's Video/Audio",
"submit-any-error-logs": "Submit any error logs",
"show-help-info": "Show Help Info",
"language-options": "Language Options",
"add-to-calendar": "Add to Calendar",
"youtube-video-demoing-how-to-do-this": "Youtube Video demoing how to do this",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Invite a guest or camera source to publish into the group room",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If disabled, the invited guest will not be able to see or hear anyone in the room.",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Use this link as a Browser Source to capture the video or audio",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If disabled, you must manually add a video to a scene for it to appear.",
"disables-echo-cancellation-and-improves-audio-quality": "Disables Echo Cancellation and improves audio quality",
"audio-only-sources-are-visually-hidden-from-scenes": "Audio-only sources are visually hidden from scenes",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"guest-will-be-prompted-to-enter-a-display-name": "Guest will be prompted to enter a Display Name",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Display Names will be shown in the bottom-left corner of videos",
"guests-not-actively-speaking-will-be-hidden": "Guests not actively speaking will be hidden",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "Request 1080p60 from the Guest instead of 720p60, if possible",
"the-default-microphone-will-be-pre-selected-for-the-guest": "The default microphone will be pre-selected for the guest",
"the-default-camera-device-will-selected-automatically": "The default camera device will selected automatically",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "The guest won't have access to changing camera settings or screenshare",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"increase-video-quality-that-guests-in-room-see-": "Increase video quality that guests in room see.",
"the-guest-will-not-see-their-own-self-preview-after-joining": "The guest will not see their own self-preview after joining",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Guests will have an option to poke the Director by pressing a button",
"add-an-audio-compressor-to-the-guest-s-microphone": "Add an audio compressor to the guest's microphone",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Add an Equalizer to the guest's microphone that the director can control",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-guest-can-only-see-the-director-s-video-if-provided": "The guest can only see the Director's video, if provided",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "The guest's microphone will be muted on joining. They can unmute themselves.",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Have the guest join muted, so only the director can Unmute the guest.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "The guest will not be asked for a video device on connection",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Make the invite URL encoded, so parameters are harder to tinker with by guests",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"move-the-user-to-another-room-controlled-by-another-director": "Move the user to another room, controlled by another director",
"send-a-direct-message-to-this-user-": "Send a Direct Message to this user.",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Force the user to Disconnect. They can always reconnect.",
"toggle-solo-voice-chat": "Toggle Solo Voice Chat",
"add-this-video-to-any-remote-scene-1-": "Add this Video to any remote '&scene=1'",
"mute-this-guest-everywhere": "Mute this guest everywhere",
"add-this-video-to-any-remote-scene-2-": "Add this Video to any remote '&scene=2'",
"remotely-mute-this-audio-in-all-remote-scene-views": "Remotely Mute this Audio in all remote '&scene' views",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"add-to-scene-8": "Add to Scene 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"solo-this-video-everywhere": "Solo this video everywhere",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"toggle-the-remote-guest-s-speaker-output": "Toggle the remote guest's speaker output",
"toggle-the-remote-guest-s-display-output": "Toggle the remote guest's display output",
"shift-this-video-down-in-order": "Shift this Video Down in Order",
"current-index-order-of-this-video": "Current Index Order of this Video",
"shift-this-video-up-in-order": "Shift this Video Up in Order",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Start Recording this remote stream to this local drive. *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "The Remote Guest will record their local stream to their local drive. *experimental*",
"remotely-change-the-volume-of-this-guest": "Remotely change the volume of this guest",
"disable-video-preview": "Disable Video Preview",
"low-quality-preview": "Low-Quality Preview",
"high-quality-preview": "High-Quality Preview",
"set-to-audio-channel-1": "Set to Audio Channel 1",
"set-to-audio-channel-2": "Set to Audio Channel 2",
"set-to-audio-channel-3": "Set to Audio Channel 3",
"set-to-audio-channel-4": "Set to Audio Channel 4",
"set-to-audio-channel-5": "Set to Audio Channel 5",
"set-to-audio-channel-6": "Set to Audio Channel 6",
"remote-audio-settings": "Remote Audio Settings",
"advanced-video-settings": "Advanced Video Settings",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"add-to-scene-2": "Add to Scene 2",
"activate-or-reload-this-video-device-": "Activate or Reload this video device.",
"tip-hold-ctrl-command-to-select-multiple": "tip: Hold CTRL (command) to select Multiple",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"cannot-see-videos": "Cannot see videos",
"cannot-hear-others": "Cannot hear others",
"see-director-only": "See director only",
"show-mini-preview": "Show Mini preview",
"raise-hand-button": "Raise hand button",
"show-labels": "Show labels",
"transfer-to-a-new-room": "Transfer to a new Room",
"enable-custom-password": "Enable custom password",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"add-group-chat-to-obs": "Add Group Chat",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "For large group rooms, this option can reduce the load on remote guests substantially",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "The director will be visible in scenes, as if a performer themselves.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Useful if you want to perform and direct at the same time",
"which-video-codec-would-you-want-used-by-default-": "Which video codec would you want used by default?",
"you-ll-enter-as-the-room-s-director": "You'll enter as the room's director",
"add-your-camera-to-obs": "Add your Camera",
"start-streaming": "start streaming",
"remote-screenshare-into-obs": "Remote Screenshare",
"create-reusable-invite": "Create Reusable Invite",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Ideal for 1080p60 gaming, if your computer and upload are up for it",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Better video compression and quality at the cost of increased CPU encoding load",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Disable digital audio-effects and increase audio bitrate",
"the-guest-will-not-have-a-choice-over-audio-options": "The guest will not have a choice over audio-options",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "The guest will only be able to select their webcam as an option",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Hold CTRL and the mouse wheel to zoom in and out remotely of compatible video streams",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "Encode the URL so that it's harder for a guest to modify the settings.",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Add a password to make the stream inaccessible to those without the password",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Add the guest to a group-chat room; it will be created automatically if needed.",
"customize-the-room-settings-for-this-guest": "Customize the room settings for this guest",
"more-options": "More Options",
"hold-ctrl-or-cmd-to-select-multiple-files": "Hold CTRL (or CMD) to select multiple files",
"enter-an-https-url": "Enter an HTTPS URL",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees"
},
"innerHTML": {
"join-room": "Join room",
"join-room-with-mic": "Join room with Microphone",
"join-room-with-camera": "Join Room with Camera",
"copy-this-url": "Copy this URL into your \"browser source\"",
"share-screen-with-room": "Screenshare with Room",
"share-your-mic": "Share your Microphone",
"share-your-camera": "Share your Camera",
"share-your-screen": "Share your Screen",
"click-start-to-join": "Click Start to Join",
"waiting-for-mic-to-load": "Waiting for mic to load",
"waiting-for-camera-to-load": "Waiting for Camera to load",
"you-are-in-the-control-center": "Control center for room:",
"joining-room": "You are in room",
"push-to-talk-enable": " enable director`s microphone or video<br>(only guests can see this feed)",
"hide-the-links": " LINKS (GUEST INVITES &amp; SCENES)",
"click-for-quick-room-overview": "\n\t\t\t\t\t\t<i class=\"las la-question-circle\"></i> <span data-translate=\"click-here-for-help\">Click Here for a quick overview and help</span>\n\t\t\t\t\t",
"click-here-for-help": "Click Here for a quick overview and help",
"welcome-to-control-room": "\n\t\t\t\t\t\t<b>Welcome. This is the director's control-room for the group-chat.</b><br><br>\n\t\t\t\t\t\tYou can host a group chat with friends using a room. Share the blue link to invite guests who will join the chat automatically.\n\t\t\t\t\t\t<br><br>\n\t\t\t\t\t\tA group room can handle normally around 6 to 20 guests, depending on numerous factors, including CPU and available bandwidth of all guests in the room\n\t\t\t\t\t",
"invite-users-to-join": "Guests can use the link to join the group room",
"guests-hear-others": "Guests hear others",
"capture-a-group-scene": "CAPTURE A GROUP SCENE",
"this-is-obs-browser-source-link": "Use studio software to capture the group video mix",
"auto-add-guests": "Auto-add guests",
"pro-audio-mode": "Pro-audio mode",
"hide-audio-only-sources": "Hide audio-only sources",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"ask-for-display-name": "Ask for display name",
"show-display-names": "Show display names",
"show-active-speaker": "Show active speakers",
"auto-select-microphone": "Auto-select default microphone",
"auto-select-camera": "Auto-select default camera",
"hide-setting-buttons": "Hide settings button",
"mini-self-preview": "Mini self-preview",
"virtual-backgrounds": "Virtual backgrounds",
"fade-videos-in": "Fade videos in",
"powerful-computers-only": "Only use with powerful computers and small groups!!",
"guests-see-HD-video": "Guests see HD video",
"no-self-preview": "Disable self-preview",
"raise-hand-button": "Display 'raise-hand' button",
"enable-compressor": "Enable audio compressor",
"enable-equalizer": "Enable equalizer as option",
"show-guest-tips": "Show guest setup tips",
"low-cpu=broadcast-codec": "Low-CPU broadcast codec",
"only-see-director-feed": "Only see the director's feed",
"mute-microphone-by-default": "Muted; guest can unmute",
"unmute-by-director-only": "Muted; director can unmute",
"guest-joins-with-no-camera": "Guest joins with no camera",
"obfuscate-link": "Obfuscate link and parameters",
"this-can-reduce-packet-loss": "Can reduce packet loss video corruption",
"use-h264-codec": "Use H264 codec",
"show-active-speakers": "Show active speakers",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"force-mono-audio": "Force mono audio",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"learn-more-about-params": "Learn more about URL parameters at ",
"forward-to-room": "Transfer",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Message",
"disconnect-guest": "Hangup",
"voice-chat": "<i class=\"las la-microphone\" style=\"color:#090\"></i> Solo Talk",
"add-to-scene": "add to scene 1",
"mute-guest": "mute guest",
"More-scene-options": "More scene options",
"add-to-scene2": "add to scene 2",
"mute-scene": "mute in scene",
"force-keyframe": "Rainbow Puke Fix",
"stats-remote": " Scene Stats",
"additional-controls": "Additional controls",
"solo-video": "Highlight guest",
"hide-guest": "hide guest",
"toggle-remote-speaker": "Deafen Guest",
"toggle-remote-display": "Blind Guest",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"change-url": "Change URL",
"change-params": "URL Params",
"record-local": " Record Local",
"record-remote": " Record Remote",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Audio Settings",
"advanced-camera-settings": "<i class=\"las la-sliders-h\"></i> Video Settings",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"video-source": " Video Source ",
"max-resolution": "Max Resolution",
"balanced": "Balanced",
"smooth-cool": "Smooth and Cool",
"select-audio-source": " Audio Source(s) ",
"select-output-source": " Audio Output Destination: ",
"select-digital-effect": " Digital Video Effects: ",
"no-effects-applied": "No effects applied",
"blurred-background": "Blurred background",
"digital-greenscreen": "Digital greenscreen",
"virtual-background": "Virtual background",
"select-local-image": "Select Local Image",
"close-settings": "Close Settings",
"advanced": "Advanced ",
"open-in-new-tab": "Open in new Tab",
"copy-to-clipboard": "Copy to Clipboard",
"send-chat": "Send",
"apply-new-guest-settings": "Apply settings",
"cancel": "Cancel",
"invisible-guests": "Not Visible",
"available-languages": "Available Languages:",
"add-more-here": "Add More Here!",
"add-to-calendar": "Add details to your Calendar:",
"add-to-google-calendar": "Add to Google Calendar",
"add-to-outlook-calendar": "Add to Outlook Calendar",
"add-to-yahoo-calendar": "Add to Yahoo Calendar",
"logo-header": "\n\t\t\t\t\t<font id=\"qos\">V</font>DO.Ninja \n\t\t\t\t",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"add-group-chat": "Create a Room",
"rooms-allow-for": "Rooms allow for group-chat and the tools to manage multiple guests.",
"room-name": "Room Name",
"password-input-field": "Password",
"guests-only-see-director": "Guests can only see the Director's Video",
"scenes-can-see-director": "Director will also be a performer",
"default-codec-select": "Preferred Video Codec: ",
"enter-the-rooms-control": "Enter the Room's Control Center",
"show-tips": "Show me some tips..",
"added-notes": "\n\t\t\t\t\t\t\t\t<u>\n\t\t\t\t\t\t\t\t\t<i>Important Tips:</i><br><br>\n\t\t\t\t\t\t\t\t</u>\n\t\t\t\t\t\t\t\t<li>Disabling video sharing between guests will improve performance</li>\n\t\t\t\t\t\t\t\t<li>Invite only guests to the room that you trust.</li>\n\t\t\t\t\t\t\t\t<li>The \"Recording\" option is considered experimental.</li>\n\t\t\t\t\t\t\t\t<li><a href=\"https://params.vdo.ninja\" style=\"color:black;\"><u>Advanced URL parameters</u></a> are available to customize rooms.</li>\n\t\t\t\t\t\t\t",
"back": "Back",
"add-your-camera": "Add your Camera",
"ask-for-permissions": "Allow Access to Camera/Microphone",
"waiting-for-camera": "Waiting for Camera to Load",
"no-audio": "No Audio",
"add-a-password": " Add a Password:",
"use-chrome-instead": "Consider using a Chromium-based browser instead.<br>\n \t\t\t\t\t\tSafari is more prone to having audio issues",
"remote-screenshare-obs": "Remote Screenshare",
"select-screen-to-share": "SELECT SCREEN TO SHARE",
"audio-sources": "Audio Sources",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"create-reusable-invite": "Create Reusable Invite",
"here-you-can-pre-generate": "Here you can pre-generate a reusable Browser Source link and a related guest invite link.",
"generate-invite-link": "GENERATE THE INVITE LINK",
"advanced-paramaters": "Advanced Options",
"unlock-video-bitrate": "Unlock Video Bitrate (20mbps)",
"force-vp9-video-codec": "Force VP9 Video Codec",
"enable-stereo-and-pro": "Enable Stereo and Pro HD Audio",
"video-resolution": "Video Resolution: ",
"hide-mic-selection": "Force Default Microphone",
"hide-screen-share": "Hide Screenshare Option",
"allow-remote-control": "Remote Control Camera Zoom (android)",
"obfuscate_url": "Obfuscate the Invite URL",
"add-a-password-to-stream": " Add a password:",
"add-the-guest-to-a-room": " Add the guest to a room:",
"invite-group-chat-type": "This room guest can:",
"can-see-and-hear": "Can see and hear the group chat",
"can-hear-only": "Can only hear the group chat",
"cant-see-or-hear": "Cannot hear or see the group chat",
"share-local-video-file": "Stream Media File",
"select-the-video-files-to-share": "SELECT THE VIDEO FILES TO SHARE",
"share-website-iframe": "Share Website",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"run-a-speed-test": "Run a Speed Test",
"read-the-guides": "Browse the Guides",
"info-blob": "",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"more-than-four-can-join": "These four guest slots are just for demonstration. More than four guests can actually join a room.",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tWelcome! You can send text messages directly to connected peers from here.\n\t\t\t\t",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast.."
},
"placeholders": {
"join-by-room-name-here": "Join by Room Name here",
"enter-your-message-here": "Enter your message here",
"enter-chat-message-to-send-here": "Enter chat message to send here",
"enter-the-room-name-here": "Enter the room name here",
"enter-the-room-password-here": "Enter the room password here",
"enter-a-room-name-here": "Enter a Room Name here",
"optional-room-password-here": "Optional room password here",
"optional": "optional",
"give-this-media-source-a-name-optional-": "Give this media source a name (optional)",
"add-an-optional-password": "Add an optional password",
"enter-room-name-here": "Enter Room name here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "Enter a new Display Name for this stream",
"submit-error-report": "Press OK to submit any error logs. Error logs may contain private information.",
"director-redirect-1": "The director wishes to redirect you to the URL: ",
"director-redirect-2": "\n\nPress OK to be redirected.",
"add-a-label": "Add a label",
"audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. See <a target='_blank' href='https://docs.vdo.ninja/director-settings/codirector'>&codirector</a> on how to become a co-director.</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.",
"director": "Director",
"unknown-user": "Unknown User",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?",
"load-previous-session": "Would you like to load your previous session's settings?",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.",
"enter-display-name": "Please enter your display name:",
"enter-new-display-name": "Enter a new Display Name for this stream",
"what-bitrate": "What bitrate would you like to record at? (kbps)\n(note: This feature is experimental, so have backup recordings going)",
"enter-website": "Enter a website URL to share",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.",
"share-with-trusted": "Share only with those you trust",
"pass-recommended": "A password is recommended",
"insecure-room-name": "Insecure room name.",
"allowed-chars": "Allowed chars",
"transfer": "transfer",
"armed": "armed",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)",
"change-url": "change URL",
"mute-in-scene": "mute in scene",
"unmute-guest": "un-mute guest",
"undeafen": "un-deafen",
"deafen": "deafen guest",
"unblind": "un-blind",
"blind": "blind guest",
"unmute": "un-mute",
"mute-guest": "mute guest",
"unhide": "unhide guest",
"hide-guest": "hide guest",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

405
cn.json Normal file
View File

@ -0,0 +1,405 @@
{
"titles": {
"join-by-room-name-here": "输入房间名称",
"join-room": "加入房间",
"load-the-next-guest-in-queue": "加载队列中的下一个客人",
"toggle-the-chat": "切换聊天",
"mute-the-speaker": "使扬声器静音",
"mute-the-mic": "使麦克风静音",
"disable-the-camera": "禁用摄像头",
"share-a-screen-with-others": "与他人共享屏幕",
"create-a-secondary-stream": "创建次要流",
"share-a-website-as-an-embedded-iframe": "将网站共享为嵌入式 iFRAME",
"room-settings": "房间设置",
"your-audio-and-video-settings": "您的音频和视频设置",
"hangup-the-call": "挂断电话",
"alert-the-host-you-want-to-speak": "提醒您要发言的主持人",
"record-your-stream-to-disk": "将您的流记录到磁盘",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "您还可以在之后通过单击设置按钮启用导演的视频输出",
"cancel-the-director-s-video-audio": "取消导演的视频/音频",
"submit-any-error-logs": "提交任何错误日志",
"show-help-info": "显示帮助信息",
"language-options": "语言选项",
"add-to-calendar": "添加到日历",
"youtube-video-demoing-how-to-do-this": "Youtube 视频演示如何做到这一点",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "邀请来宾或摄像机源发布到小组房间",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "如果禁用,受邀客人将无法看到或听到房间内的任何人。",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "使用 OBS 浏览器源中的此链接捕获视频或音频",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "如果禁用,您必须手动将视频添加到场景中才能显示。",
"disables-echo-cancellation-and-improves-audio-quality": "禁用回声消除并提高音频质量",
"audio-only-sources-are-visually-hidden-from-scenes": "纯音频源在视觉上隐藏在场景中",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "允许通过监控工具进行远程统计监控",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "重新访问时将询问客人是否要重新加载上一个链接",
"guest-will-be-prompted-to-enter-a-display-name": "将提示客人输入显示名称",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "显示名称将显示在视频的左下角",
"guests-not-actively-speaking-will-be-hidden": "不主动发言的客人将被隐藏",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "如果可能,请从访客请求 1080p60 而不是 720p60",
"the-default-microphone-will-be-pre-selected-for-the-guest": "将为客人预先选择默认麦克风",
"the-default-camera-device-will-selected-automatically": "将自动选择默认相机设备",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "客人将无权更改相机设置或屏幕共享",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "客人的自拍视频预览将在右上角显示很小",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "允许客人选择虚拟背景幕效果",
"videos-use-an-animated-transition-when-being-remixed": "视频在重新混合时使用动画过渡",
"increase-video-quality-that-guests-in-room-see-": "提高客房客人看到的视频质量。",
"the-guest-will-not-see-their-own-self-preview-after-joining": "客人加入后将看不到自己的自我预览",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "客人可以选择按一个按钮来戳导演",
"add-an-audio-compressor-to-the-guest-s-microphone": "为客人的麦克风添加音频压缩器",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "为客人的麦克风添加一个导演可以控制的均衡器",
"show-some-prep-suggestions-to-the-guests-on-connect": "在 connect 上向客人显示一些准备建议",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "这种低保真视频编解码器使用很少的 CPU即使有几十个活跃的观众。",
"the-guest-can-only-see-the-director-s-video-if-provided": "客人只能看到导演的视频(如果提供)",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "客人的麦克风将在加入时静音。他们可以自己取消静音。",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "将客人加入静音,这样只有导演才能取消客人静音。",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "连接时不会要求客人提供视频设备",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "对邀请 URL 进行编码,这样来宾更难修改参数",
"the-active-speakers-are-made-visible-automatically": "活动扬声器自动可见",
"set-the-background-color-to-bright-green": "将背景颜色设置为亮绿色",
"fade-videos-in-over-500ms": "淡入淡出超过 500 毫秒的视频",
"add-a-10px-margin-around-all-video-elements": "在所有视频元素周围添加 10 像素的边距",
"playback-the-video-with-mono-channel-audio": "使用单声道音频播放视频",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "让视频适合各自的区域,即使这意味着裁剪一点",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "让视频与专为垂直视频设计的尺寸对齐",
"copy-this-stream-id-to-the-clipboard": "将此流 ID 复制到剪贴板",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "单击此处编辑此流的标签。更改将传播到此流的所有查看者",
"move-the-user-to-another-room-controlled-by-another-director": "将用户移动到另一个房间,由另一个导演控制",
"send-a-direct-message-to-this-user-": "向该用户发送私信。",
"force-the-user-to-disconnect-they-can-always-reconnect-": "强制用户断开连接。他们总是可以重新连接。",
"toggle-solo-voice-chat": "Toggle 单独语音聊天",
"add-this-video-to-any-remote-scene-1-": "将此视频添加到任何远程 '&场景=1'",
"mute-this-guest-everywhere": "到处都让这位客人静音",
"add-this-video-to-any-remote-scene-2-": "将此视频添加到任何远程“&场景=2”",
"remotely-mute-this-audio-in-all-remote-scene-views": "在所有远程“&场景”视图中远程静音此音频",
"add-to-scene-3": "添加到场景 3",
"add-to-scene-4": "添加到场景 4",
"add-to-scene-5": "添加到场景 5",
"add-to-scene-6": "添加到场景 6",
"add-to-scene-7": "添加到场景 7",
"add-to-scene-8": "添加到场景 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "强制远程发送器向所有场景发出关键帧,修复像素拖尾问题。",
"request-the-statistics-of-this-video-in-any-active-scene": "在任何活动场景中请求此视频的统计信息",
"solo-this-video-everywhere": "随处播放此视频",
"hide-this-guest-everywhere": "到处隐藏这位客人",
"toggle-the-remote-guest-s-speaker-output": "切换远程客人的扬声器输出",
"toggle-the-remote-guest-s-display-output": "切换远程访客的显示输出",
"shift-this-video-down-in-order": "按顺序向下移动此视频",
"current-index-order-of-this-video": "此视频的当前索引顺序",
"shift-this-video-up-in-order": "按顺序上移此视频",
"remotely-reload-the-guest-s-page-with-a-new-url": "使用新 URL 远程重新加载访客页面",
"change-user-parameters": "更改用户参数",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "开始将此远程流录制到此本地驱动器。 *实验性的*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "远程访客将他们的本地流记录到他们的本地驱动器。 *实验性*",
"remotely-change-the-volume-of-this-guest": "远程更改此客人的音量",
"disable-video-preview": "禁用视频预览",
"low-quality-preview": "低质量预览",
"high-quality-preview": "高质量预览",
"set-to-audio-channel-1": "设置为音频通道 1",
"set-to-audio-channel-2": "设置为音频通道 2",
"set-to-audio-channel-3": "设置为音频通道 3",
"set-to-audio-channel-4": "设置为音频通道 4",
"set-to-audio-channel-5": "设置为音频通道 5",
"set-to-audio-channel-6": "设置为音频通道 6",
"remote-audio-settings": "远程音频设置",
"advanced-video-settings": "高级视频设置",
"this-will-ask-the-remote-guest-for-permission-to-change": "这将要求远程来宾允许更改",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "视频/音频流的直接独奏视图,没有别的。它的音频可以从这里远程控制",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "这位客人举手了。单击此按钮可清除通知。",
"add-to-scene-2": "添加到场景 2",
"activate-or-reload-this-video-device-": "激活或重新加载此视频设备。",
"tip-hold-ctrl-command-to-select-multiple": "提示:按住 CTRL命令选择多个",
"improve-performance-and-quality-with-this-tip": "使用此技巧提高性能和质量",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "增加这一点会带来危险。更改每位访客的总入站视频比特率;移动设备除外。 Webp 模式也排除在外。",
"cannot-see-videos": "看不到视频",
"cannot-hear-others": "听不见别人",
"see-director-only": "只见导演",
"show-mini-preview": "显示迷你预览",
"raise-hand-button": "举手按钮",
"show-labels": "显示标签",
"transfer-to-a-new-room": "转移到新房间",
"enable-custom-password": "启用自定义密码",
"hide-this-window": "隐藏此窗口",
"cycle-the-cameras": "这将要求远程客人允许更改7",
"add-group-chat-to-obs": "这将要求远程客人允许更改8",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "对于大型团体房间,此选项可以大大减轻远程客人的负担】【导演将在场景中可见,就像表演者本人一样。】【如果你想同时表演和导演很有用】【哪个视频默认情况下,您希望使用编解码器吗?",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "您将作为房间的主管进入",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "添加您的相机",
"which-video-codec-would-you-want-used-by-default-": "开始流式传输",
"you-ll-enter-as-the-room-s-director": "远程屏幕共享",
"add-your-camera-to-obs": "创建可重复使用的邀请",
"start-streaming": "非常适合 1080p60 游戏,如果您的计算机和上传都已启动为此",
"remote-screenshare-into-obs": "以增加 CPU 编码负载为代价获得更好的视频压缩和质量",
"create-reusable-invite": "禁用数字音频效果并提高音频比特率",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "客人将无法选择音频选项",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "您将作为房间的主管进入0",
"disable-digital-audio-effects-and-increase-audio-bitrate": "您将作为房间的主管进入1",
"the-guest-will-not-have-a-choice-over-audio-options": "您将作为房间的主管进入2",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "您将作为房间的主管进入3",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "您将作为房间的主管进入4",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "您将作为房间的主管进入5",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "您将作为房间的主管进入6",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "您将作为房间的主管进入7",
"customize-the-room-settings-for-this-guest": "您将作为房间的主管进入8",
"more-options": "更多选项",
"hold-ctrl-or-cmd-to-select-multiple-files": "按住 CTRL或 CMD选择多个文件",
"enter-an-https-url": "输入 HTTPS URL",
"creative-commons-by-3-0": "知识共享 3.0",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "屏幕共享流 ID 使用可预测的前缀值而不是随机值。",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "切换独奏语音聊天或在选择将其设为双向私密时按住 CTRL/CMD。",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "相机将以默认安全模式加载,如果其他模式失败,该模式可能会工作.",
"disable-animated-transitions-during-video-mixing": "在视频混合期间禁用动画过渡",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "允许来宾选择要上传给导演的文件。分享后,它将在聊天中显示为下载链接.",
"set-a-countdown-timer-that-this-guest-sees": "设置此客人看到的倒数计时器",
"enter-a-room-name-to-quick-join": "Enter a room name to quick join"
},
"innerHTML": {
"copy-this-url": "将此 URL 复制到 OBS“浏览器源”中",
"you-are-in-the-control-center": "房间控制中心:",
"joining-room": "你在房间",
"push-to-talk-enable": " 启用导演的麦克风或视频<br>(只有客人才能看到此提要)",
"hide-the-links": " 链接(参会者邀请和场景)",
"click-for-quick-room-overview": "\n\t\t\t\t\t\t<i class=\"las la-question-circle\"></i> <span data-translate=\"click-here-for-help\">单击此处获取快速概览和帮助</span>\n\t\t\t\t\t",
"click-here-for-help": "单击此处获取快速概览和帮助",
"welcome-to-control-room": "\n\t\t\t\t\t\t<b>欢迎。这是群聊的主管控制室。</b><br><br>\n\t\t\t\t\t\t您可以使用房间与朋友进行群聊。分享蓝色链接以邀请将自动加入聊天的客人。\n\t\t\t\t\t\t<br><br>\n\t\t\t\t\t\t<font style=\"color:red\">群组聊天室的已知限制:</font><br>\n\t\t\t\t\t\t<li>一个团体房间最多可容纳约 30 位客人,具体取决于多种因素,包括房间内所有客人的 CPU 和可用带宽。不过,要达到超过 7 位客人的人数,您可能需要 <a href=\"https://www.youtube.com/watch?v=bpRa8-UYCGc\" title=\"\nYoutube 视频演示如何做到这一点\">禁用客人之间的视频共享</a>使用 &amp;broadcast、&amp;roombitrate=0 或 &amp;novideo 是那里的选项。</li>\n\t\t\t\t\t\t\n\t\t\t\t\t\t<li>视频会故意为客人和导演呈现低质量的视频这是为了节省带宽和CPU资源。尽管如此它在 OBS 中仍然是高质量的。</li>\n\t\t\t\t\t\t\n\t\t\t\t\t\t<li>当导演重置控制室或场景时,场景状态(例如场景中哪些视频处于活动状态)将丢失。</li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\t补充说明<br><br>\n\t\t\t\t\t\t<li>每个访客视频的单独观看链接在视频加载时提供。这些可以在 OBS 浏览器源中使用。</li>\n\t\t\t\t\t\t<li>您可以使用自动混合组场景(绿色链接)在 OBS 中为您自动排列多个视频。</li>\n\t\t\t\t\t\t<li>您可以使用此控制室来录制隔离的视频或音频流,但它仍然是一项实验性功能。</li>\n\t\t\t\t\t\t<li>如果您将客人从一个房间转移到另一个房间,他们将不知道他们被转移到哪个房间。</li>\n\t\t\t\t\t\t<li>OBS 将看到客人的高质量视频;默认视频比特率为 2500kbps。设置更高的比特率将改善运动。</li>\n\t\t\t\t\t\t<li>VP8 通常是默认的视频编解码器,但在 OBS 中使用 &amp;codec=vp9 或 &amp;codec=h264 作为 URL 可以帮助减少损坏的视频呕吐问题</li>\n\t\t\t\t\t\t<li>&amp;stereo=2 可以添加到guests 以关闭音频效果,例如回声消除和降噪。</li>\n\t\t\t\t\t\t<li>https://invite.cam 是一项免费服务,可以帮助混淆提供给客人的邀请链接的 URL 参数。</li>\n\t\t\t\t\t\t<li>S Virtual Camera for exampl</li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\n\t\t\t\t\t\t对于高级 URL 选项和参数,<a href=\"https://docs.vdo.ninja/advanced-settings\">查看WIKI</a>\n\t\t\t\t\t",
"invite-users-to-join": "客人可以使用链接加入团体房间",
"guests-hear-others": "客人听到别人的声音",
"capture-a-group-scene": "拍摄集体场景",
"this-is-obs-browser-source-link": "在 OBS 或其他演播室软件中使用以捕捉群组视频混音",
"auto-add-guests": "自动添加客人",
"pro-audio-mode": "专业音频模式",
"hide-audio-only-sources": "隐藏纯音频源",
"remote-monitoring": "邀请已保存到 cookie",
"invite-saved-to-cookie": "邀请保存到 cookie",
"ask-for-display-name": "要求显示名称",
"show-display-names": "显示显示名称",
"show-active-speaker": "显示当前发言者",
"auto-select-microphone": "自动选择默认麦克风",
"auto-select-camera": "自动选择默认相机",
"hide-setting-buttons": "隐藏设置按钮",
"mini-self-preview": "迷你自我预览",
"virtual-backgrounds": "虚拟背景",
"fade-videos-in": "淡入淡出视频",
"guests-see-HD-video": "参会者观看高清视频",
"no-self-preview": "禁用个人预览",
"raise-hand-button": "举手按钮",
"enable-compressor": "启用压缩机",
"enable-equalizer": "启用均衡器",
"show-guest-tips": "显示客人提示",
"low-cpu=broadcast-codec": "低CPU编码",
"only-see-director-feed": "只看导演提要",
"fill-video-space": "裁剪视频以适合",
"vertical-aspect-ratio": "竖屏视频模式",
"learn-more-about-params": "了解更多参数 ",
"forward-to-room": "转移",
"send-direct-chat": "<i class=\"las la-envelope\"></i> 信息",
"disconnect-guest": "挂断",
"voice-chat": "<i class=\"las la-microphone\" style=\"color:#090\"></i> 独奏",
"add-to-scene": "添加到场景1",
"mute-guest": "静音客人",
"More-scene-options": "更多场景选择",
"add-to-scene2": "添加到场景2",
"mute-scene": "现场静音",
"force-keyframe": "像素拖尾修复",
"stats-remote": " 场景统计",
"additional-controls": "附加控件",
"solo-video": "突出客人",
"hide-guest": "隐藏客人",
"toggle-remote-speaker": "静音客人",
"toggle-remote-display": "切换摄像头显示",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"change-url": "更改链接地址",
"change-params": "网址参数",
"record-local": "本地录制",
"record-remote": "访客录制",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> 音频设置",
"advanced-camera-settings": "<i class=\"las la-sliders-h\"></i> 视频设置",
"user-raised-hand": "手放下",
"unmute": "取消静音",
"unhide-guest": "取消隐藏",
"undeafen": "开启声音",
"unblind": "开启视频",
"close": "关闭",
"send-message": "发送信息<s pan=\"\"> </s>",
"record-director-local": " 记录",
"video-source": " 视频源 ",
"max-resolution": "最大分辨率",
"balanced": "均衡",
"smooth-cool": "流畅顺滑",
"select-audio-source": " 音频源(s) ",
"select-output-source": " 音频输出目的地: ",
"select-digital-effect": " 数字视频效果: ",
"no-effects-applied": "没有应用效果",
"blurred-background": "模糊背景",
"digital-greenscreen": "数字绿幕",
"virtual-background": "虚拟背景",
"select-local-image": "选择本地图片",
"close-settings": "关闭设置",
"advanced": "先进的 ",
"open-in-new-tab": "在新标签页中打开",
"copy-to-clipboard": "复制到剪贴板",
"send-chat": "发送",
"apply-new-guest-settings": "应用设置",
"cancel": "取消",
"invisible-guests": "不可见",
"available-languages": "可用语言:",
"add-more-here": "添加更多!",
"add-to-calendar": "添加到日历:",
"add-to-google-calendar": "添加到谷歌日历",
"add-to-outlook-calendar": "添加到outlook日历",
"add-to-yahoo-calendar": "添加到雅虎日历",
"logo-header": "\n\t\t\t\t\t<font id=\"qos\">V</font>DO.Ninja \n\t\t\t\t",
"only-director-can-hear-you": "目前只有导演能听到你的声音.",
"director-muted-you": "导演把你静音了.",
"add-group-chat": "创建房间",
"rooms-allow-for": "房间允许群聊和管理多个客人的工具。",
"room-name": "房间名称",
"password-input-field": "密码",
"guests-only-see-director": "客人只能看到导演的视频",
"scenes-can-see-director": "导演也将是表演者",
"default-codec-select": "首选视频编解码器: ",
"enter-the-rooms-control": "进入房间的控制中心",
"show-tips": "告诉我一些提示..",
"added-notes": "\n\t\t\t\t\t\t\t\t<u>\n\t\t\t\t\t\t\t\t\t<i>Important Tips:</i><br><br>\n\t\t\t\t\t\t\t\t</u>\n\t\t\t\t\t\t\t\t<li>Disabling video sharing between guests will improve performance</li>\n\t\t\t\t\t\t\t\t<li>Invite only guests to the room that you trust.</li>\n\t\t\t\t\t\t\t\t<li>The \"Recording\" option is considered experimental.</li>\n\t\t\t\t\t\t\t\t<li><a href=\"https://params.vdo.ninja\" style=\"color:black;\"><u>Advanced URL parameters</u></a> are available to customize rooms.</li>\n\t\t\t\t\t\t\t",
"back": "后退",
"add-your-camera": "添加您的相机",
"ask-for-permissions": "允许访问相机和麦克风",
"waiting-for-camera": "等待相机加载",
"no-audio": "无音频",
"add-a-password": " 添加密码:",
"use-chrome-instead": "考虑改用基于 Chromium 的浏览器.<br>\n \t\t\t\t\t\tSafari 更容易出现音频问题",
"remote-screenshare-obs": "远程屏幕共享",
"select-screen-to-share": "选择要分享的屏幕",
"audio-sources": "音频源",
"application-audio-capture": "有关特定于应用程序的音频捕获, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">请参见此处</a>",
"create-reusable-invite": "创建可重复使用的邀请",
"here-you-can-pre-generate": "在这里你可以预先生成一个可重用的浏览器源链接和相关的访客邀请链接.",
"generate-invite-link": "生成邀请链接",
"advanced-paramaters": "高级选项",
"unlock-video-bitrate": "解锁视频比特率 (20mbps)",
"force-vp9-video-codec": "强制 VP9 视频编解码器",
"enable-stereo-and-pro": "启用立体声和专业高清音频",
"video-resolution": "视频分辨率: ",
"hide-mic-selection": "强制默认麦克风",
"hide-screen-share": "隐藏屏幕共享选项",
"allow-remote-control": "遥控相机变焦 (android)",
"obfuscate_url": "混淆邀请 URL",
"add-a-password-to-stream": " 添加密码:",
"add-the-guest-to-a-room": " 将客人添加到房间:",
"invite-group-chat-type": "这个房间的客人可以:",
"can-see-and-hear": "可以看到和听到群聊",
"can-hear-only": "只能听到群聊",
"cant-see-or-hear": "无法听到或看到群聊",
"share-local-video-file": "流媒体文件",
"select-the-video-files-to-share": "选择要共享的视频文件",
"share-website-iframe": "分享网站",
"enter-the-website-URL-you-wish-to-share": "输入您要分享的网址.",
"run-a-speed-test": "运行速度测试",
"read-the-guides": "浏览指南",
"info-blob": "",
"animate-mixing": "动画混合",
"prefix-screenshare": "前缀屏幕共享 ID",
"more-than-four-can-join": "这四个来宾插槽仅用于演示。超过四位客人实际上可以加入一个房间。",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\t欢迎您可以从这里直接向连接的对等方发送短信.\n\t\t\t\t",
"privacy-disabled": "隐私警告:如果您继续,导演将能够远程访问您的摄像头和麦克风.",
"face-mesh": "面网 (slow load)",
"anonymous-mask": "匿名面具",
"dog-face": "狗耳朵和鼻子",
"compatibility-mode": "兼容模式",
"disable-animated-mixing": "禁用动画",
"powerful-computers-only": "仅用于功能强大的计算机和小组!!",
"mute-microphone-by-default": "默认麦克风静音",
"unmute-by-director-only": "只允许导演取消静音",
"guest-joins-with-no-camera": "客人无需摄像头即可加入",
"obfuscate-link": "加密链接和参数",
"this-can-reduce-packet-loss": "减少视频丢包",
"use-h264-codec": "使用H264编码",
"show-active-speakers": "显示当前发言者",
"green-background": "绿色背景",
"add-margin": "添加视频边框",
"force-mono-audio": "强制单声道音频",
"request-upload": " 请求文件",
"create-timer": "创建计时器",
"edit-url": "手动编辑网址",
"mirror-video": "镜子",
"toggle-control-video": "切换控制栏",
"picture-in-picture": "画中画",
"chrome-cast": "Cast..",
"join-room": "Join room",
"join-room-with-mic": "Join room with Microphone",
"join-room-with-camera": "Join Room with Camera",
"share-screen-with-room": "Screenshare with Room",
"share-your-mic": "Share your Microphone",
"share-your-camera": "Share your Camera",
"share-your-screen": "Share your Screen",
"click-start-to-join": "Click Start to Join",
"waiting-for-mic-to-load": "Waiting for mic to load",
"waiting-for-camera-to-load": "Waiting for Camera to load"
},
"placeholders": {
"join-by-room-name-here": "用房间名称加入",
"enter-your-message-here": "在此输入你的消息",
"enter-chat-message-to-send-here": "在此处输入要发送的聊天消息",
"enter-the-room-name-here": "在此输入房间名称",
"enter-the-room-password-here": "在此输入房间密码",
"enter-a-room-name-here": "在此处输入房间名称",
"optional-room-password-here": "可选房间密码在这里",
"optional": "可选的",
"give-this-media-source-a-name-optional-": "为该媒体源命名 (optional)",
"add-an-optional-password": "添加可选密码",
"enter-room-name-here": "在此处输入房间名称"
},
"miscellaneous": {
"start": "开始",
"new-display-name": "为流输入新的显示名称",
"submit-error-report": "按 OK 将错误日志提交到 VDO.Ninja。错误日志可能包含私人信息.",
"director-redirect-1": "主管希望将您重定向到以下链接: ",
"director-redirect-2": "\n\n按确定重定向.",
"add-a-label": "添加标签",
"audio-processing-disabled": "此访客禁用音频处理。无法静音或更改音量",
"not-the-director": "<font color='red'>你不是这个房间的管理员。无法控制房间。你可以在第一个管理员离开后尝试认领房间.</font>",
"room-is-claimed": "房间管理权限已移交他人.\n\n只有第一个加入房间的人是指定的管理员.\n\n第一个管理员离开后刷新.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "您要发布的流地址已经被使用.\n\n请尝试使用其他邀请链接或刷新以重试.\n\n您现在将被断开连接.",
"director": "导向器",
"unknown-user": "未知用户",
"room-test-not-good": "房间命名为test非常常用可能不安全.\n\n您确定要继续吗?",
"load-previous-session": "您想加载上一个对话的设置吗?",
"enter-password": "请在下方输入密码: \n\n(注意:密码区分大小写,如果不正确,您将不会收到警告.)",
"enter-password-2": "请在下方输入密码: \n\n(注意:密码区分大小写.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "密码不正确.\n\n请刷新并重试.",
"enter-display-name": "请输入您的显示名称:",
"enter-new-display-name": "为此流创建一个新名称",
"what-bitrate": "您想以什么码率录制? (kbps)",
"enter-website": "输入要共享的网站链接",
"press-ok-to-record": "按确定键开始录制. 再次按下会停止并下载.\n\n警告保持此浏览器选项卡处于活动状态以继续录制.\n\n如果需要您可以在下面更改默认视频比特率 (kbps)",
"no-streamID-provided": "未提供流地址; 一个将随机生成.\n\n流地址: ",
"alphanumeric-only": "提示:流地址只能使用字母数字字符.\n\n违规字符已替换为下划线",
"stream-id-too-long": "流 ID 的长度应少于45个数字或字母符号.\n\n我们将它修剪到一定长度.",
"share-with-trusted": "只与您信任的人分享",
"pass-recommended": "建议设置密码",
"insecure-room-name": "不安全的房间名称.",
"allowed-chars": "允许的字符",
"transfer": "转移",
"armed": "武装",
"transfer-guest-to-room": "转移客人到房间:\n\n请注意房间必须共享相同的密码",
"transfer-guest-to-url": "将客人转移到新的网站 URL。\n\n会提示客人接受",
"change-url": "更改网址",
"mute-in-scene": "现场静音",
"unmute-guest": "取消静音客人",
"undeafen": "非聋哑人",
"deafen": "聋哑人",
"unblind": "非盲人",
"blind": "盲人",
"unmute": "取消静音",
"mute-guest": "静音客人",
"unhide": "取消隐藏客人",
"hide-guest": "隐藏客人",
"confirm-disconnect-users": "您确定要断开这些用户的连接吗?",
"confirm-disconnect-user": "您确定要断开此用户的连接吗?"
}
}

View File

@ -1,235 +1,235 @@
<head>
<link rel="stylesheet" href="./main.css?ver=40" />
<style>
.container {
max-width: 80%;
width: fit-content;
margin: 0 auto;
}
h1 {
margin-top: 1em;
margin-bottom: 1em;
padding: 10px;
}
.card {
margin: 10px;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%);
background-color: #ddd;
color: black;
margin-bottom: 1.5em;
}
.card>div {
padding: 10px;
}
.card h2 {
font-size: 1.5em;
padding: 10px;
background-color: #457b9d;
color: white;
border-bottom: 2px solid #3b6a87;
}
small {
font-style: italic;
display: block;
margin-left: 1em;
}
span.warning {
color: rgb(212, 191, 0);
}
input {
padding: 10px;
}
video {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
audio {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
div#processing {
display: none;
justify-content: center;
place-items: center;
position: absolute;
inset: 0;
font-size: 1.5em;
font-weight: bold;
background: #141926;
flex-direction: column;
}
</style>
</head>
<body style='color:white'>
<div id="header">
<a id="logoname" href="./" style="text-decoration: none; color: white; margin: 2px">
<span data-translate="logo-header">
<font id="qos">V</font>DO.Ninja
</span>
</a>
</div>
<div class="container">
<div id="info">
<h1>Web-based Media Conversion Tools</h1>
<div class="card">
<h2>WebM to MP4 (fixed 1280x720 resolution) <span class='warning'>(very slow!)</span></h2>
<div>
<small>The same as: fmpeg -i input.webm -vf scale=1280:720 output.mp4</small>
<input type="file" id="uploader" title="Convert WebM to MP4">
</div>
</div>
<div class="card">
<h2>MKV to MP4 (no transcoding)</h2>
<div>
<small>The same as: fmpeg -i INPUTFILE -vcodec copy -acodec copy output.mp4</small>
<input type="file" id="uploader2" accept=".mkv" title="Convert MKV to MP4">
</div>
</div>
<div class="card">
<h2>WebM MP4 files (no transcoding, attempts to force high resolutions)</h2>
<div>
<input type="file" id="uploader3" accept=".webm" title="Convert WebM to MP4">
</div>
</div>
<div class="card">
<h2>WebM to Audio-only files (opus or wav)</h2>
<div>
<input type="file" id="uploader4" accept=".webm" title="Convert WebM to OPUS">
</div>
</div>
<div id="processing">
<span id="message"></span>
<video id="player" controls style="display:none"></video>
<audio id="player2" controls style="display:none"></audio>
</div>
</div>
<script>
if (window.location.hostname.indexOf("vdo.ninja") == 0) {
window.location = window.location.href.replace("vdo.ninja","isolated.vdo.ninja"); // FFMPEG requires an isolated domain.
}
</script>
<script src="./thirdparty/ffmpeg.min.js"></script>
<script>
function download(data, filename) {
const blob = new Blob([data.buffer]);
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
}
const { createFFmpeg, fetchFile } = FFmpeg;
const ffmpeg = createFFmpeg({ log: true });
const transcode = async ({ target: { files } }) => {
const { name } = files[0];
document.getElementById('uploader').style.display = "none";
document.getElementById('uploader2').style.display = "none";
document.getElementById('uploader3').style.display = "none";
document.getElementById('message').innerText = "Transcoding file... this will take a while";
document.getElementById('processing').style.display = 'flex';
await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
await ffmpeg.run('-i', name, '-vf', 'scale=1280:720', 'output.mp4');
const data = ffmpeg.FS('readFile', 'output.mp4');
const video = document.getElementById('player');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
video.style.display = "block";
document.getElementById('message').innerText = "Operation Done. Play video or download it.";
}
const transmux = async ({ target: { files } }) => {
const { name } = files[0];
document.getElementById('uploader').style.display = "none";
document.getElementById('uploader2').style.display = "none";
document.getElementById('uploader3').style.display = "none";
document.getElementById('message').innerText = "Transcoding file... this will take a while";
document.getElementById('processing').style.display = 'flex';
await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
await ffmpeg.run('-i', name, '-vcodec', 'copy', '-acodec', 'copy', 'output.mp4');
const data = ffmpeg.FS('readFile', 'output.mp4');
const video = document.getElementById('player');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
video.style.display = "block";
document.getElementById('message').innerText = "Operation Done. Play video or download it.";
}
const force1080 = async ({ target: { files } }) => {
const { name } = files[0];
const sourceBuffer = await fetch("./media/cap.webm").then(r => r.arrayBuffer());
document.getElementById('uploader').style.display = "none";
document.getElementById('uploader2').style.display = "none";
document.getElementById('uploader3').style.display = "none";
document.getElementById('message').innerText = "Tweaking file... this will take a moment";
document.getElementById('processing').style.display = 'flex';
await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
ffmpeg.FS("writeFile", "cap.webm", new Uint8Array(sourceBuffer, 0, sourceBuffer.byteLength));
await ffmpeg.run("-i", "concat:cap.webm|" + name, "-safe", "0", "-c", "copy", "-avoid_negative_ts", "1", "-strict", "experimental", "output.mp4");
const data = ffmpeg.FS('readFile', 'output.mp4');
const video = document.getElementById('player');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
video.style.display = "block";
document.getElementById('message').innerText = "Operation Done. Play video or download it.";
document.getElementById('processing').style.display = 'flex';
}
const convertToAudioOnly = async ({ target: { files } }) => {
const { name } = files[0];
document.getElementById('message').innerText = "Transcoding file... this will take a while";
document.getElementById('processing').style.display = 'flex';
await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
const video = document.getElementById('player');
await ffmpeg.run('-i', name, '-vn', '-acodec', 'copy', 'output.opus');
const data = ffmpeg.FS('readFile', 'output.opus');
console.log(data.buffer.byteLength);
if (data.buffer.byteLength) {
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'audio/opus' }));
download(data, name.split(".")[0] + ".opus");
} else {
await ffmpeg.run('-i', name, '-vn', '-acodec', 'copy', 'output.wav');
const data2 = ffmpeg.FS('readFile', 'output.wav');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'audio/pcm' }));
download(data2, name.split(".")[0] + ".wav");
}
video.style.display = "block";
document.getElementById('message').innerText = "Operation Done. Play audio or download it.";
document.getElementById('processing').style.display = 'flex';
}
document.getElementById('uploader').addEventListener('change', transcode);
document.getElementById('uploader2').addEventListener('change', transmux);
document.getElementById('uploader3').addEventListener('change', force1080);
document.getElementById('uploader4').addEventListener('change', convertToAudioOnly);
</script>
<head>
<link rel="stylesheet" href="./main.css?ver=40" />
<style>
.container {
max-width: 80%;
width: fit-content;
margin: 0 auto;
}
h1 {
margin-top: 1em;
margin-bottom: 1em;
padding: 10px;
}
.card {
margin: 10px;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%);
background-color: #ddd;
color: black;
margin-bottom: 1.5em;
}
.card>div {
padding: 10px;
}
.card h2 {
font-size: 1.5em;
padding: 10px;
background-color: #457b9d;
color: white;
border-bottom: 2px solid #3b6a87;
}
small {
font-style: italic;
display: block;
margin-left: 1em;
}
span.warning {
color: rgb(212, 191, 0);
}
input {
padding: 10px;
}
video {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
audio {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
div#processing {
display: none;
justify-content: center;
place-items: center;
position: absolute;
inset: 0;
font-size: 1.5em;
font-weight: bold;
background: #141926;
flex-direction: column;
}
</style>
</head>
<body style='color:white'>
<div id="header">
<a id="logoname" href="./" style="text-decoration: none; color: white; margin: 2px">
<span data-translate="logo-header">
<font id="qos">V</font>DO.Ninja
</span>
</a>
</div>
<div class="container">
<div id="info">
<h1>Web-based Media Conversion Tools</h1>
<div class="card">
<h2>WebM to MP4 (fixed 1280x720 resolution) <span class='warning'>(very slow!)</span></h2>
<div>
<small>The same as: fmpeg -i input.webm -vf scale=1280:720 output.mp4</small>
<input type="file" id="uploader" title="Convert WebM to MP4">
</div>
</div>
<div class="card">
<h2>WebM to MP4 files (no transcoding, attempts to force high resolutions)</h2>
<div>
<input type="file" id="uploader3" accept=".webm" title="Convert WebM to MP4">
</div>
</div>
<div class="card">
<h2>WebM to Audio-only files (opus or wav)</h2>
<div>
<input type="file" id="uploader4" accept=".webm" title="Convert WebM to OPUS">
</div>
</div>
<div class="card">
<h2>MKV to MP4 (no transcoding)</h2>
<div>
<small>The same as: fmpeg -i INPUTFILE -vcodec copy -acodec copy output.mp4</small>
<input type="file" id="uploader2" accept=".mkv" title="Convert MKV to MP4">
</div>
</div>
<div id="processing">
<span id="message"></span>
<video id="player" controls style="display:none"></video>
<audio id="player2" controls style="display:none"></audio>
</div>
</div>
<script>
if (window.location.hostname.indexOf("vdo.ninja") == 0) {
window.location = window.location.href.replace("vdo.ninja","isolated.vdo.ninja"); // FFMPEG requires an isolated domain.
}
</script>
<script src="./thirdparty/ffmpeg.min.js"></script>
<script>
function download(data, filename) {
const blob = new Blob([data.buffer]);
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
}
const { createFFmpeg, fetchFile } = FFmpeg;
const ffmpeg = createFFmpeg({ log: true });
const transcode = async ({ target: { files } }) => {
const { name } = files[0];
document.getElementById('uploader').style.display = "none";
document.getElementById('uploader2').style.display = "none";
document.getElementById('uploader3').style.display = "none";
document.getElementById('message').innerText = "Transcoding file... this will take a while";
document.getElementById('processing').style.display = 'flex';
await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
await ffmpeg.run('-i', name, '-vf', 'scale=1280:720', 'output.mp4');
const data = ffmpeg.FS('readFile', 'output.mp4');
const video = document.getElementById('player');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
video.style.display = "block";
document.getElementById('message').innerText = "Operation Done. Play video or download it.";
}
const transmux = async ({ target: { files } }) => {
const { name } = files[0];
document.getElementById('uploader').style.display = "none";
document.getElementById('uploader2').style.display = "none";
document.getElementById('uploader3').style.display = "none";
document.getElementById('message').innerText = "Transcoding file... this will take a while";
document.getElementById('processing').style.display = 'flex';
await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
await ffmpeg.run('-i', name, '-vcodec', 'copy', '-acodec', 'copy', 'output.mp4');
const data = ffmpeg.FS('readFile', 'output.mp4');
const video = document.getElementById('player');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
video.style.display = "block";
document.getElementById('message').innerText = "Operation Done. Play video or download it.";
}
const force1080 = async ({ target: { files } }) => {
const { name } = files[0];
const sourceBuffer = await fetch("./media/cap.webm").then(r => r.arrayBuffer());
document.getElementById('uploader').style.display = "none";
document.getElementById('uploader2').style.display = "none";
document.getElementById('uploader3').style.display = "none";
document.getElementById('message').innerText = "Tweaking file... this will take a moment";
document.getElementById('processing').style.display = 'flex';
await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
ffmpeg.FS("writeFile", "cap.webm", new Uint8Array(sourceBuffer, 0, sourceBuffer.byteLength));
await ffmpeg.run("-i", "concat:cap.webm|" + name, "-safe", "0", "-c", "copy", "-avoid_negative_ts", "1", "-strict", "experimental", "output.mp4");
const data = ffmpeg.FS('readFile', 'output.mp4');
const video = document.getElementById('player');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
video.style.display = "block";
document.getElementById('message').innerText = "Operation Done. Play video or download it.";
document.getElementById('processing').style.display = 'flex';
}
const convertToAudioOnly = async ({ target: { files } }) => {
const { name } = files[0];
document.getElementById('message').innerText = "Transcoding file... this will take a while";
document.getElementById('processing').style.display = 'flex';
await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
const video = document.getElementById('player');
await ffmpeg.run('-i', name, '-vn', '-acodec', 'copy', 'output.opus');
const data = ffmpeg.FS('readFile', 'output.opus');
console.log(data.buffer.byteLength);
if (data.buffer.byteLength) {
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'audio/opus' }));
download(data, name.split(".")[0] + ".opus");
} else {
await ffmpeg.run('-i', name, '-vn', '-acodec', 'copy', 'output.wav');
const data2 = ffmpeg.FS('readFile', 'output.wav');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'audio/pcm' }));
download(data2, name.split(".")[0] + ".wav");
}
video.style.display = "block";
document.getElementById('message').innerText = "Operation Done. Play audio or download it.";
document.getElementById('processing').style.display = 'flex';
}
document.getElementById('uploader').addEventListener('change', transcode);
document.getElementById('uploader2').addEventListener('change', transmux);
document.getElementById('uploader3').addEventListener('change', force1080);
document.getElementById('uploader4').addEventListener('change', convertToAudioOnly);
</script>
</body>

425
cs.json Normal file
View File

@ -0,0 +1,425 @@
{
"titles": {
"toggle-the-chat": "Vypnout/zapnout chat",
"mute-the-speaker": "Vypnout mikrofon mluvčího",
"mute-the-mic": "Vypnout mikrofon",
"disable-the-camera": "Vypnout kameru",
"settings": "Nastavení",
"hangup-the-call": "Zavěsit hovor",
"show-help-info": "Zobrazit menu pomoci",
"language-options": "Jazyková nastavení",
"tip-hold-ctrl-command-to-select-multiple": "tip: Podržte Ctrl (command), abyste vybrali více najednou",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Ideální pro 1080p60 gaming, pokud vám na to vystačí prostředky počítače",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Lepší komprese videa a kvalita za cenu vyšší zátěže procesoru",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Zakázat digitální zvukové efekty a zvýšit přenosovou rychlost zvuku",
"the-guest-will-not-have-a-choice-over-audio-options": "Host nebude mít na výběr z možností zvuku",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "The guest will only be able to select their webcam as an option",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Podržte CTRL a kolečko myši pro vzdálené přiblížení a oddálení kompatibilních video streamů",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Přidejte heslo, aby nebyl stream přístupný pro ty, kteří nemají heslo",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Přidejte hosta do skupinové chatovací místnosti; v případě potřeby se vytvoří automaticky.",
"customize-the-room-settings-for-this-guest": "Upravte nastavení místnosti pro tohoto hosta",
"hold-ctrl-or-cmd-to-select-multiple-files": "Podržením klávesy CTRL (nebo CMD) vyberte více souborů",
"enter-an-https-url": "Zadejte URL s HTTPS",
"lucy-g": "Lucy G",
"flaticon": "Flaticon",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"gregor-cresnar": "Gregor Cresnar",
"add-this-video-to-any-remote-scene-1-": "Přidejte toto video k jakémukoliv cílovému rozhraní '&scene=1'",
"forward-user-to-another-room-they-can-always-return-": "Přesměrujte uživatele na jiný room. Uživatel se může kdykoliv vrátit.",
"start-recording-this-stream-experimental-views": "Začít nahrávat tento stream. *experimentální*'",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Odpojit tohoto uživatele. Uživatel se může kdykoliv připojit zpět.",
"change-this-audio-s-volume-in-all-remote-scene-views": "Změnit toto audio ve všech cílových '&scene' pohledech.",
"remotely-mute-this-audio-in-all-remote-scene-views": "Vzdáleně zlumit toto audio ve všech cílových '&scene' pohledech.",
"disable-video-preview": "Vypnout náhled videa",
"low-quality-preview": "Náhled videa v nízké kvalitě",
"high-quality-preview": "Náhled videa ve vysoké kvalitě",
"send-direct-message": "Poslat přímou zprávu",
"advanced-settings-and-remote-control": "Pokročilá nastavení a vzálené ovládání",
"toggle-voice-chat-with-this-guest": "Vypnout/zapnout voice chat tohoto hosta ",
"join-by-room-name-here": "Enter a room name to quick join",
"join-room": "Join room",
"share-a-screen-with-others": "Share a Screen with others",
"alert-the-host-you-want-to-speak": "Alert the host you want to speak",
"record-your-stream-to-disk": "Record your stream to disk",
"cancel-the-director-s-video-audio": "Cancel the Director's Video/Audio",
"submit-any-error-logs": "Submit any error logs",
"add-group-chat-to-obs": "Add Group Chat to OBS",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "For large group rooms, this option can reduce the load on remote guests substantially",
"which-video-codec-would-you-want-used-by-default-": "Which video codec would you want used by default?",
"you-ll-enter-as-the-room-s-director": "You'll enter as the room's director",
"add-your-camera-to-obs": "Add your Camera to OBS",
"remote-screenshare-into-obs": "Remote Screenshare into OBS",
"create-reusable-invite": "Create Reusable Invite",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "Encode the URL so that it's harder for a guest to modify the settings.",
"more-options": "More Options",
"youtube-video-demoing-how-to-do-this": "Youtube Video demoing how to do this",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Invite a guest or camera source to publish into the group room",
"if-enabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If enabled, the invited guest will not be able to see or hear anyone in the room.",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Use this link in the OBS Browser Source to capture the video or audio",
"if-enabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If enabled, you must manually add a video to a scene for it to appear.",
"disables-echo-cancellation-and-improves-audio-quality": "Disables Echo Cancellation and improves audio quality",
"audio-only-sources-are-visually-hidden-from-scenes": "Audio-only sources are visually hidden from scenes",
"guest-will-be-prompted-to-enter-a-display-name": "Guest will be prompted to enter a Display Name",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Display Names will be shown in the bottom-left corner of videos",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "Request 1080p60 from the Guest instead of 720p60, if possible",
"the-default-microphone-will-be-pre-selected-for-the-guest": "The default microphone will be pre-selected for the guest",
"the-default-camera-device-will-selected-automatically": "The default camera device will selected automatically",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "The guest won't have access to changing camera settings or screenshare",
"the-guest-will-not-see-their-own-self-preview-after-joining": "The guest will not see their own self-preview after joining",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Guests will have an option to poke the Director by pressing a button",
"add-an-audio-compressor-to-the-guest-s-microphone": "Add an audio compressor to the guest's microphone",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Add an Equalizer to the guest's microphone that the director can control",
"the-guest-can-only-see-the-director-s-video-if-provided": "The guest can only see the Director's video, if provided",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "The guest's microphone will be muted on joining. They can unmute themselves.",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Have the guest join muted, so only the director can Unmute the guest.",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Make the invite URL encoded, so parameters are harder to tinker with by guests",
"move-the-user-to-another-room-controlled-by-another-director": "Move the user to another room, controlled by another director",
"send-a-direct-message-to-this-user-": "Send a Direct Message to this user.",
"remotely-change-the-volume-of-this-guest": "Remotely change the volume of this guest",
"mute-this-guest-everywhere": "Mute this guest everywhere",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Start Recording this remote stream to this local drive. *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "The Remote Guest will record their local stream to their local drive. *experimental*",
"shift-this-video-down-in-order": "Shift this Video Down in Order",
"current-index-order-of-this-video": "Current Index Order of this Video",
"shift-this-video-up-in-order": "Shift this Video Up in Order",
"remote-audio-settings": "Remote Audio Settings",
"advanced-video-settings": "Advanced Video Settings",
"activate-or-reload-this-video-device-": "Activate or Reload this video device.",
"load-the-next-guest-in-queue": "Load the next guest in queue",
"create-a-secondary-stream": "Create a Secondary Stream",
"add-to-calendar": "Add to Calendar",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "The director will be visible in scenes, as if a performer themselves.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Useful if you want to perform and direct at the same time",
"start-streaming": "start streaming",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If disabled, the invited guest will not be able to see or hear anyone in the room.",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If disabled, you must manually add a video to a scene for it to appear.",
"guests-not-actively-speaking-will-be-hidden": "Guests not actively speaking will be hidden",
"increase-video-quality-that-guests-in-room-see-": "Increase video quality that guests in room see.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "The guest will not be asked for a video device on connection",
"toggle-solo-voice-chat": "Toggle Solo Voice Chat",
"add-to-scene-2": "Add to Scene 2",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"set-to-default-audio-channel": "Set to Default Audio Channel",
"set-to-audio-channel-1": "Set to Audio Channel 1",
"set-to-audio-channel-2": "Set to Audio Channel 2",
"set-to-audio-channel-3": "Set to Audio Channel 3",
"set-to-audio-channel-4": "Set to Audio Channel 4",
"set-to-audio-channel-5": "Set to Audio Channel 5",
"toggle-the-remote-guest-s-speaker-output": "Toggle the remote guest's speaker output",
"toggle-the-remote-guest-s-display-output": "Toggle the remote guest's display output",
"set-to-audio-channel-6": "Set to Audio Channel 6",
"set-to-audio-channel-7": "Set to Audio Channel 7",
"set-to-audio-channel-8": "Set to Audio Channel 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"solo-this-video-everywhere": "Solo this video everywhere",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"cannot-see-videos": "Cannot see videos",
"cannot-hear-others": "Cannot hear others",
"see-director-only": "See director only",
"show-mini-preview": "Show Mini preview",
"raise-hand-button": "Raise hand button",
"show-labels": "Show labels",
"transfer-to-a-new-room": "Transfer to a new Room",
"enable-custom-password": "Enable custom password",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"add-this-video-to-any-remote-scene-2-": "Add this Video to any remote '&scene=2'",
"add-to-scene-8": "Add to Scene 8",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees",
"enter-a-room-name-to-quick-join": "Enter a room name to quick join"
},
"innerHTML": {
"logo-header": "<font id=\"qos\" style=\"color: white;\">O</font>BS.Ninja ",
"copy-this-url": "Zkopírujte tuhle URL do OBS \"Browser Source\"",
"you-are-in-the-control-center": "Jsi v administraci místnosti",
"joining-room": "Připojujete se",
"add-group-chat": "Přidat skupinový chat OBS",
"rooms-allow-for": "Místnosti umožnůjí jednoduchý skupinový chat a pokročilou správu více streamů zároveň.",
"room-name": "Jméno místnost",
"password-input-field": "Heslo",
"enter-the-rooms-control": "Vstoupit do administrace místnosti",
"show-tips": "Zobrazit tipy..",
"added-notes": "\n\t\t\t\t<u><i>Poznámka:</i></u>\n\t\t\t\t<li>Kdokoliv se může připojit, když zná jméno místnosti</li>\n\t\t\t\t<li>Není doporučeno mít v místnosti víc než 4 lidi kvůli náročnosti na Váš počítač, ale za zkoušku nic nedáte.</li>\n\t\t\t\t<li>iOS zařízení jsou omezena pouze na dva účastníky. Toto omezení je způsobeno Applem.</li>\n\t\t\t\t<li> \"Nahrávat\" je nová a EXPERIMENTÁLNÍ funkce.</li>\n\t\t\t\t<li>Musíte \"Přidat\" zdroj video do \"Skupinová scéna\" aby se tu zobrazil.</li>\n\t\t\t\t<li>Tady je nové \"vylepšený fullscreen\" tlačítko přidané pro hostitele.</li>\n\t\t\t\t",
"back": "Zpět",
"add-your-camera": "Přidat kameru do OBS",
"ask-for-permissions": "Allow Access to Camera/Microphone",
"waiting-for-camera": "Čekám na načtení kamery",
"video-source": "Video zdroj",
"max-resolution": "MAX rozlišení",
"balanced": "Vyrovnaný",
"smooth-cool": "Super a Cool",
"select-audio-source": "Zvolit zdroj Audia",
"no-audio": "Žádné Audio",
"select-output-source": " Audio výstup: \n\t\t\t\t\t",
"remote-screenshare-obs": "Vzdálené sdílení obrazovky do OBS",
"note-share-audio": "\n\t\t\t\t\t<b>poznámka</b>: Nezapomeňte zakliknout \"Sdílet audio\" v Chromu.<br>(Firefox nepodporuje sdílení zvuku.)",
"select-screen-to-share": "VYBRAT OBRAZOVKU KE SDÍLENÍ",
"audio-sources": "Audio zdroje",
"create-reusable-invite": "Vytvořit pozvánku na více použití",
"here-you-can-pre-generate": "Zde můžete generovat linky do OBS i pozvánky na více použití.",
"generate-invite-link": "GENEROVAT POZVÁNKU",
"advanced-paramaters": "Pokročilé nastavení",
"unlock-video-bitrate": "Rozvolnit limit Video Bitrate (20mbps)",
"force-vp9-video-codec": "Vynutit VP9 Video Codec (méně artefaktů)",
"enable-stereo-and-pro": "Povolit Stereo a Pro HD Audio",
"video-resolution": "Rozlišení videa: ",
"hide-mic-selection": "Vynutit výchozí mikrofon",
"hide-screen-share": "Nezobrazovat možnost sdílet obrazovku",
"allow-remote-control": "Vzdálené ovládání přiblížení (android)",
"add-a-password-to-stream": " Přidat heslo:",
"add-the-guest-to-a-room": " Přidat hosta do místosti:",
"invite-group-chat-type": "Člen této místnosti může:",
"can-see-and-hear": "Slyšet a vidět ostatní členy",
"can-hear-only": "Pouze slyšet ostatní členy",
"cant-see-or-hear": "Neslyšet ani nevidět ostatní členy",
"share-local-video-file": "Streamovat mediální soubor",
"share-website-iframe": "Sdílet webovou stránku",
"run-a-speed-test": "Zapnout speed test",
"read-the-guides": "Procházejte průvodce",
"info-blob": "\n\t\t\t\t\t\t<h2>Co je OBS.Ninja</h2><br>\n\t\t\t\t\t\t<li>100% <b>zdarma</b>; bez stahování; žádné osobní data; bez přihlašování</li>\n\t\t\t\t\t\t<li>Sdílejte video ze smartphonu, laptopu, počítače či svých kamarádů přímo do OBSka</li>\n\t\t\t\t\t\t<li>Používáme nejmodernější Peer-to-Peer forwarding technologii, která zaručuje bezpečnost a minimální lag</li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\t<li>Youtube video <i class=\"fa fa-youtube-play\" aria-hidden=\"true\"></i> <a href=\"https://www.youtube.com/watch?v=6R_sQKxFAhg\">Demo</a> </li>",
"add-to-scene": "Add to Scene",
"forward-to-room": "Transfer",
"record": "Nahrávat",
"disconnect-guest": "Hangup",
"mute": "Ztišit",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Chatovat",
"advanced-camera-settings": "<i class=\"las la-cog\"></i> Pokročilé",
"voice-chat": "<i class=\"las la-microphone\"></i> Voice Chat",
"open-in-new-tab": "Otevřít v nové záložce",
"copy-to-clipboard": "Kopírovat do schránky",
"click-for-quick-room-overview": "❔ Klidni zde pro krátký přehled o funkcích",
"push-to-talk-enable": "🔊 Povolit administrátorovi Push-to-Talk mód",
"welcome-to-control-room": "Welcome. This is the control-room for the group-chat. There are different things you can use this room for:<br><br>\t<li>You can host a group chat with friends using a room. Share the blue link to invite guests who will join the chat automatically.</li>\t<li>A group room can handle around 4 to 30 guests, depending on numerous factors, including CPU and available bandwidth of all guests in the room.</li>\t<li>Solo-views of each video are offered under videos as they load. These can be used within an OBS Browser Source.</li>\t<li>You can use the auto-mixing Group Scene, the green link, to auto arrange multiple videos for you in OBS.</li>\t<li>You can use this control room to record isolated video or audio streams, but it is an experimental feature still.</li>\t<li>Videos in the Director's room will be of low quality on purpose; to save bandwidth/CPU</li>\t<li>Guest's in the room will see each other's videos at a very limited quality to conserve bandwidth/CPU.</li>\t<li>OBS will see a guest's video in high-quality; the default video bitrate is 2500kbps.</li>\t<br>\tAs guests join, their videos will appear below. You can bring their video streams into OBS as solo-scenes or you can add them to the Group Scene.\t<br>The Group Scene auto-mixes videos that have been added to the group scene. Please note that the Auto-Mixer requires guests be manually added to it for them to appear in it; they are not added automatically.<br><br>Apple mobile devices, such as iPhones and iPads, do not fully support Video Group Chat. This is a hardware constraint.<br><br>\tFor advanced options and parameters, <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">see the Wiki.</a>",
"more-than-four-can-join": "Tyto čtyři sloty pro hosty slouží pouze k předvedení. K místnosti se mohou skutečně připojit více než čtyři hosté.",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tVítejte na OBS.Ninja! můžete ihned poslat zprávy ostatním členům této místnosti\n\t\t\t\t",
"names-and-labels-coming-soon": "\n\t\t\t\t\tJména členů bude jedna z budoucích funkcí OBS.ninja.\n\t\t\t\t",
"send-chat": "Poslat",
"available-languages": "Dostupné jazyky:",
"add-more-here": "Přidat další!",
"waiting-for-camera-to-load": "Čekám na načtení kamery",
"start": "START",
"share-your-mic": "Sdílet mikrofon",
"share-your-camera": "Sdílet kameru",
"share-your-screen": "Sdílet obrazovku",
"join-room-with-mic": "Připojit se s mikrofonem",
"share-screen-with-room": "Sdílet obrazovku s místností",
"join-room-with-camera": "Připojit se s kamerou",
"click-start-to-join": "Kliknutím na start se připojíte",
"guests-only-see-director": "Guests can only see the Director's Video",
"default-codec-select": "Preferred Video Codec: ",
"obfuscate_url": "Obfuscate the Invite URL",
"hide-the-links": " LINKS (GUEST INVITES &amp; SCENES)",
"invite-users-to-join": "Guests can use the link to join the group room",
"this-is-obs-browser-source-link": "Use in OBS or other studio software to capture the group video mix",
"mute-scene": "mute in scene",
"mute-guest": "mute guest",
"record-local": " Record Local",
"record-remote": " Record Remote",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Audio Settings",
"scenes-can-see-director": "Director will also be a performer",
"select-digital-effect": " Digital Video Effects: ",
"add-a-password": " Add a Password:",
"hide-guest": "hide guest",
"toggle-remote-speaker": "Deafen Guest",
"toggle-remote-display": "Blind Guest",
"force-keyframe": "Rainbow Puke",
"change-url": "Change URL",
"change-params": "URL Params",
"solo-video": "Highlight guest",
"stats-remote": " Scene Stats",
"apply-new-guest-settings": "Apply settings",
"cancel": "Cancel",
"add-to-calendar": "Add details to your Calendar:",
"no-effects-applied": "No effects applied",
"blurred-background": "Blurred background",
"digital-greenscreen": "Digital greenscreen",
"virtual-background": "Virtual background",
"use-chrome-instead": "Consider using a Chromium-based browser instead.<br>\n \t\t\t\t\t\tSafari is more prone to having audio issues",
"select-the-video-files-to-share": "SELECT THE VIDEO FILES TO SHARE",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"click-here-for-help": "Click Here for a quick overview and help",
"guests-hear-others": "Guests hear others",
"capture-a-group-scene": "CAPTURE A GROUP SCENE",
"auto-add-guests": "Auto-add guests",
"pro-audio-mode": "Pro-audio mode",
"hide-audio-only-sources": "Hide audio-only sources",
"ask-for-display-name": "Ask for display name",
"show-display-names": "Show display names",
"show-active-speaker": "Show active speakers",
"auto-select-microphone": "Auto-select default microphone",
"auto-select-camera": "Auto-select default camera",
"hide-setting-buttons": "Hide settings button",
"mini-self-preview": "Mini self-preview",
"virtual-backgrounds": "Virtual backgrounds",
"powerful-computers-only": "Only use with powerful computers and small groups!!",
"guests-see-HD-video": "Guests see HD video",
"no-self-preview": "Disable self-preview",
"raise-hand-button": "Display 'raise-hand' button",
"enable-compressor": "Enable audio compressor",
"enable-equalizer": "Enable equalizer as option",
"low-cpu=broadcast-codec": "Low-CPU broadcast codec",
"only-see-director-feed": "Only see the director's feed",
"mute-microphone-by-default": "Mute microphone by default",
"guest-joins-with-no-camera": "Guest joins with no camera",
"unmute-by-director-only": "Unmute by director only",
"obfuscate-link": "Obfuscate link and parameters",
"this-can-reduce-packet-loss": "This can reduce video corruption caused by packet loss",
"use-h264-codec": "Use H264 codec",
"show-active-speakers": "Show active speakers",
"force-mono-audio": "Force mono audio",
"learn-more-about-params": "Learn more about URL parameters at ",
"More-scene-options": "More scene options",
"additional-controls": "Additional controls",
"select-local-image": "Select Local Image",
"close-settings": "Close Settings",
"advanced": "Advanced ",
"invisible-guests": "Not Visible",
"add-to-google-calendar": "Add to Google Calendar",
"add-to-outlook-calendar": "Add to Outlook Calendar",
"add-to-yahoo-calendar": "Add to Yahoo Calendar",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"fade-videos-in": "Fade videos in",
"show-guest-tips": "Show guest setup tips",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"add-to-scene2": "add to scene 2",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast..",
"join-room": "Join room",
"waiting-for-mic-to-load": "Waiting for mic to load"
},
"placeholders": {
"join-by-room-name-here": "Připojit se s názvem místnosti zde",
"enter-a-room-name-here": "Sem zadejte název místnosti",
"optional-room-password-here": "Volitelné heslo místnosti zde",
"give-this-media-source-a-name-optional-": "Pojmenujte tento zdroj médií (volitelné)",
"add-an-optional-password": "Přidat volitelné heslo",
"enter-room-name-here": "Sem zadejte název místnosti",
"enter-chat-message-to-send-here": "Sem zadejte vaši zprávu",
"optional": "optional",
"enter-the-room-name-here": "Enter the room name here",
"enter-the-room-password-here": "Enter the room password here",
"enter-your-message-here": "Enter your message here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "Enter a new Display Name for this stream",
"submit-error-report": "Press OK to submit any error logs to VDO.Ninja. Error logs may contain private information.",
"director-redirect-1": "The director wishes to redirect you to the URL: ",
"director-redirect-2": "\n\nPress OK to be redirected.",
"add-a-label": "Add a label",
"audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. You can try claiming the room after the first director leaves.</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.",
"director": "Director",
"unknown-user": "Unknown User",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?",
"load-previous-session": "Would you like to load your previous session's settings?",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.",
"enter-display-name": "Please enter your display name:",
"enter-new-display-name": "Enter a new Display Name for this stream",
"what-bitrate": "What bitrate would you like to record at? (kbps)",
"enter-website": "Enter a website URL to share",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.",
"share-with-trusted": "Share only with those you trust",
"pass-recommended": "A password is recommended",
"insecure-room-name": "Insecure room name.",
"allowed-chars": "Allowed chars",
"transfer": "transfer",
"armed": "armed",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)",
"change-url": "change URL",
"mute-in-scene": "mute in scene",
"unmute-guest": "un-mute guest",
"undeafen": "un-deafen",
"deafen": "deafen guest",
"unblind": "un-blind",
"blind": "blind guest",
"unmute": "un-mute",
"mute-guest": "mute guest",
"unhide": "unhide guest",
"hide-guest": "hide guest",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

425
de.json Normal file
View File

@ -0,0 +1,425 @@
{
"titles": {
"toggle-the-chat": "Chat an/aus",
"mute-the-speaker": "Audio stumm",
"mute-the-mic": "Mikrofon stumm",
"disable-the-camera": "Kamera aus",
"settings": "Einstellungen",
"hangup-the-call": "Beenden",
"show-help-info": "Hilfe anzeigen",
"language-options": "Sprachoptionen",
"tip-hold-ctrl-command-to-select-multiple": "Tipp: Für Mehrfachauswahl halten Sie CTRL (Mac: command) gedrückt",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Ideal für Gaming mit 1080p60, falls Ihr Computer und Anschluss ausreichen",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Höhere Videokompression und bessere Bildqualität, benötigt mehr CPU",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Audiofilter aus, höhere Audiobitrate",
"the-guest-will-not-have-a-choice-over-audio-options": "Gäste können Audio-Optionen nicht ändern",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "Gäste können nur ihre Webcam auswählen",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Drücken Sie CTRL (Mac: command) während sie das Mausrad drehen, um kompatible Kameras ein/auszuzoomen",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Passwort hinzufügen. Dem Stream kann ohne Passwort nicht beigetreten werden",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Gast zu Gruppenraum hinzufügen. Falls nötig, wird der Raum automatisch erzeugt.",
"customize-the-room-settings-for-this-guest": "Raum-Einstellungen für diesen Gast anpassen",
"hold-ctrl-or-cmd-to-select-multiple-files": "Drücken Sie CTRL (Mac: command), um mehrere Dateien auszuwählen",
"enter-an-https-url": "Geben Sie eine URL mit HTTPS ein",
"lucy-g": "Lucy G",
"flaticon": "Flaticon",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"gregor-cresnar": "Gregor Cresnar",
"add-this-video-to-any-remote-scene-1-": "Fügen Sie dieses Video zu einer Remote-Szene '&scene=1' hinzu",
"forward-user-to-another-room-they-can-always-return-": "Transferieren Sie den Gast in einen anderen Raum. Gäste können immer hierher zurückkehren.",
"start-recording-this-stream-experimental-views": "Diesen Stream aufnehmen. *experimentell*",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Verbindung des Gastes abbrechen. Gäste können jederzeit wieder beitreten.",
"change-this-audio-s-volume-in-all-remote-scene-views": "Laustärke in allen '&scene'-Szenen ändern",
"remotely-mute-this-audio-in-all-remote-scene-views": "Audio mute in allen remote '&scene'-Szenen",
"disable-video-preview": "Videovorschau aus",
"low-quality-preview": "Videovorschau (niedrige Qualität)",
"high-quality-preview": "Videovorschau (hohe Qualität)",
"send-direct-message": "Private Message schicken",
"advanced-settings-and-remote-control": "Erweiterte Einstellungen / Remote-Einstellungen",
"toggle-voice-chat-with-this-guest": "Sprach-Chat mit Gast ein/aus",
"join-by-room-name-here": "Geben Sie einen Raumnamen ein",
"join-room": "Raum betreten",
"share-a-screen-with-others": "Bildschirm teilen",
"alert-the-host-you-want-to-speak": "Teilen Sie dem Gastgeber mit, dass Sie sprechen möchten",
"record-your-stream-to-disk": "Eigenen Stream lokal aufnehmen",
"cancel-the-director-s-video-audio": "Gastgeber-Audio/Video aus",
"submit-any-error-logs": "Fehlerprotokoll schicken",
"add-group-chat-to-obs": "Video-Gruppenchat zu OBS hinzufügen",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "Bei größeren Gruppen kann diese Option die Systemlast bei den Gästen deutlich reduzieren",
"which-video-codec-would-you-want-used-by-default-": "Welchen Codec soll standardmäßig verwendet werden?",
"you-ll-enter-as-the-room-s-director": "Sie werden dem Gruppenchat als Gastgeber beitreten",
"add-your-camera-to-obs": "Kamera zu OBS hinzufügen",
"remote-screenshare-into-obs": "Bildschirm zu OBS hinzufügen",
"create-reusable-invite": "Wiederverwendbare Einladung erzeugen",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "URL verschleiern. Das macht es für Gäste schwieriger, Einstellungen zu ändern.",
"more-options": "Weitere Einstelllungen",
"youtube-video-demoing-how-to-do-this": "Erklär-Video",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Gast oder Kamera zum Video-Gruppenchat einladen",
"if-enabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "Gast kann andere Gäste im Gruppenchat weder sehen noch hören.",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Verwenden Sie diesen Link in OBS (Quelle: Browser), um das Video und/oder Audio hinzuzufügen.",
"if-enabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "Video muss manuell zu Szenen hinzugefügt werden.",
"disables-echo-cancellation-and-improves-audio-quality": "Echo-Unterdrückung aus. Verbessert die Audioqualität",
"audio-only-sources-are-visually-hidden-from-scenes": "Audioquellen ohne Video werden in der Szene nicht angezeigt",
"guest-will-be-prompted-to-enter-a-display-name": "Gäste werden beim Betreten des Videochat gebeten, einen Namen einzugeben",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Namen werden in der linken unteren Ecke des Videos angezeigt",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "1080p60 anfordern, falls das Gast-System dies unterstützt (Standard ist 720p60)",
"the-default-microphone-will-be-pre-selected-for-the-guest": "Standard-Mikrofon wird ausgewählt",
"the-default-camera-device-will-selected-automatically": "Standard-Kamera wird ausgewählt",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "Gast kann weder Kamera-Einsstellungen ändern, noch den Bildschirm teilen",
"the-guest-will-not-see-their-own-self-preview-after-joining": "Eigene Gast-Videovorschau nach Betreten des Videochat aus",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Gäste können sich über einen Button beim Gastgeber bemerkbar machen",
"add-an-audio-compressor-to-the-guest-s-microphone": "Audiokompression",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Equalizer an/aus (wird vom Gastgeber eingestellt)",
"the-guest-can-only-see-the-director-s-video-if-provided": "Gast kann nur Video vom Gastgeber sehen, falls dieser Video zurückschickt",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "Gast-Mikrofon wird beim Betreten des Videochat stummgeschaltet. Gäste können die Stummschaltung aufheben.",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Gast-Mikrofon wird beim Betreten des Videochat stummgeschaltet. Nur der Gastgeber kann die Stummschaltung aufheben.",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Einladungs-Link verschleiern, um dem Spieltrieb der Gäste etwas Einhalt zu gebieten.",
"move-the-user-to-another-room-controlled-by-another-director": "Gast in einen anderen Videochat transferieren, der von einem anderen Gastgeber kontrolliert wird",
"send-a-direct-message-to-this-user-": "Private Nachricht schicken.",
"remotely-change-the-volume-of-this-guest": "Gast-Lautstärke remote ändern",
"mute-this-guest-everywhere": "Gast überall stummschalten",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Remote-Stream lokal (auf diesem Rechner) aufnehmen *experimentell*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "Remote-Stream remote (beim Gast) aufnehmen *experimentell*",
"shift-this-video-down-in-order": "Anzeigpriorität/-reihenfolge für dieses Video nach unten verschieben",
"current-index-order-of-this-video": "Aktuelle Anzeigepriorität",
"shift-this-video-up-in-order": "Anzeigepriorität/-reihenfolge für dieses Video nach oben verschieben",
"remote-audio-settings": "Remote Audio-Einstellungen",
"advanced-video-settings": "Erweiterte Video-Einstellungen",
"activate-or-reload-this-video-device-": "Kamera aktivieren oder neu laden.",
"load-the-next-guest-in-queue": "Load the next guest in queue",
"create-a-secondary-stream": "Create a Secondary Stream",
"add-to-calendar": "Add to Calendar",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "The director will be visible in scenes, as if a performer themselves.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Useful if you want to perform and direct at the same time",
"start-streaming": "start streaming",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If disabled, the invited guest will not be able to see or hear anyone in the room.",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If disabled, you must manually add a video to a scene for it to appear.",
"guests-not-actively-speaking-will-be-hidden": "Guests not actively speaking will be hidden",
"increase-video-quality-that-guests-in-room-see-": "Increase video quality that guests in room see.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "The guest will not be asked for a video device on connection",
"toggle-solo-voice-chat": "Toggle Solo Voice Chat",
"add-to-scene-2": "Add to Scene 2",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"set-to-default-audio-channel": "Set to Default Audio Channel",
"set-to-audio-channel-1": "Set to Audio Channel 1",
"set-to-audio-channel-2": "Set to Audio Channel 2",
"set-to-audio-channel-3": "Set to Audio Channel 3",
"set-to-audio-channel-4": "Set to Audio Channel 4",
"set-to-audio-channel-5": "Set to Audio Channel 5",
"toggle-the-remote-guest-s-speaker-output": "Toggle the remote guest's speaker output",
"toggle-the-remote-guest-s-display-output": "Toggle the remote guest's display output",
"set-to-audio-channel-6": "Set to Audio Channel 6",
"set-to-audio-channel-7": "Set to Audio Channel 7",
"set-to-audio-channel-8": "Set to Audio Channel 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"solo-this-video-everywhere": "Solo this video everywhere",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"cannot-see-videos": "Cannot see videos",
"cannot-hear-others": "Cannot hear others",
"see-director-only": "See director only",
"show-mini-preview": "Show Mini preview",
"raise-hand-button": "Raise hand button",
"show-labels": "Show labels",
"transfer-to-a-new-room": "Transfer to a new Room",
"enable-custom-password": "Enable custom password",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"add-this-video-to-any-remote-scene-2-": "Add this Video to any remote '&scene=2'",
"add-to-scene-8": "Add to Scene 8",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees",
"enter-a-room-name-to-quick-join": "Enter a room name to quick join"
},
"innerHTML": {
"logo-header": "<font id=\"qos\" style=\"color: white;\">O</font>BS Ninja",
"copy-this-url": "Link für dieses Video teilen",
"you-are-in-the-control-center": "Control-Center für Video-Gruppenchat",
"joining-room": "Sie betreten Raum",
"add-group-chat": "Gruppenchat hinzufügen",
"rooms-allow-for": "Videochat-Räume erlauben einfachen Gruppen-Chat und die Verwaltung verschiedener Streams.",
"room-name": "Raum",
"password-input-field": "Passwort",
"enter-the-rooms-control": "Control Center für diesen Raum betreten",
"show-tips": "Ich möchte Tipps..",
"added-notes": "\n\t\t\t\t<u><i>Weitere Infos:</i></u>\n\t\t\t\t<li>Räume können von allen betreten werden, die den Raumnamen wissen. Vermeiden Sie daher zu einfache Namen.</li>\n\t\t\t\t<li>Je nach Hardwareausstattung können mehr als vier Teilnehmende in einem Raum zu Performance-Problemen führen.</li>\n\t\t\t\t<li>Aufgrund einer Hardware-Einschränkung können iOS-Devices Video nur mit dem Regisseur/Director teilen.</li>\n\t\t\t\t<li>Bitte betrachten Sie die \"Aufnehmen\"-Funktion als neu und experimentell. Sie sollten sie vermutlich nicht in Produktivumgebungen einsetzen.</li>\n\t\t\t\t<li>Damit ein Video-Feed in einer Gruppen-Szene erscheint, müssen Sie ihn zunächst dort hinzufügen.</li>\n\t\t\t\t<li>Der Gäste-View enthält einen neuen \"fortgeschrittenen Fullscreen\"-Button.</li>\n\t\t\t\t",
"back": "Zurück",
"add-your-camera": "Kamera hinzufügen",
"ask-for-permissions": "Zugriff für Kamera/Mikrofon erlauben",
"waiting-for-camera": "Kamera lädt. Bitte warten.",
"video-source": "Videoquelle",
"max-resolution": "Höchste Auflösung",
"balanced": "Ausgeglichen",
"smooth-cool": "Smooth und Cool",
"select-audio-source": "Audio-Quellen auswählen",
"no-audio": "Kein Audio",
"select-output-source": " Ausgang für Audio: \n\t\t\t\t\t",
"remote-screenshare-obs": "Bildschirm teilen",
"note-share-audio": "\n\t\t\t\t\t<b>note</b>: Vergessen Sie nicht, in Chrome \"Audio teilen\" zu klicken.<br>(Firefox unterstütz das Teilen von Audio nicht.)",
"select-screen-to-share": "ZU TEILENDEN BILDSCHIRM AUSWÄHLEN",
"audio-sources": "Audioquellen",
"create-reusable-invite": "Wiederverwendbare Einladung erstellen",
"here-you-can-pre-generate": "Erzeugen Sie einen wiederwendbaren Empfangs-Link und die zugehörigen Einladungslinks für Gäste.",
"generate-invite-link": "EINLADUNGS-LINK ERSTELLEN",
"advanced-paramaters": "Weitere Einstellungen",
"unlock-video-bitrate": "Video-Bitrate auf Maximum (20mbps)",
"force-vp9-video-codec": "VP9 Video- Codec verwenden (weniger Störungen)",
"enable-stereo-and-pro": "Stereo und Pro HD Audio einschalten",
"video-resolution": "Video-Auflösung: ",
"hide-mic-selection": "Force Default Microphone",
"hide-screen-share": "Option zum Bildschirm teilen nicht anzeigen",
"allow-remote-control": "Gast-Kamera zoomen (Android)",
"add-a-password-to-stream": " Passwort hinzufügen:",
"add-the-guest-to-a-room": " Gast zu diesem Raum hinzufügen:",
"invite-group-chat-type": "Dieser Gast..",
"can-see-and-hear": "..kann den Gruppen-Chat sehen und hören",
"can-hear-only": "..kann den Gruppen-Chat nur hören",
"cant-see-or-hear": "..kann den Gruppen-chat weder hören noch sehen",
"share-local-video-file": "Mediendatei streamen",
"share-website-iframe": "Website teilen",
"run-a-speed-test": "Speed-Test",
"read-the-guides": "Guides",
"info-blob": "",
"add-to-scene": "Zur Szene hinzufügen",
"forward-to-room": "Transferieren",
"record": "Aufnehmen",
"disconnect-guest": "Auflegen",
"mute": "Stummschalten",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Message",
"advanced-camera-settings": "<i class=\"las la-cog\"></i> Erweitert",
"voice-chat": "<i class=\"las la-microphone\"></i> Voice Chat",
"open-in-new-tab": "In neuem Tab öffnen.",
"copy-to-clipboard": "In die Zwischenablage kopieren",
"click-for-quick-room-overview": "❔ Erste Schritte und Hilfe",
"push-to-talk-enable": "🔊 Audio und Video mit Gästen teilen (Push-to-Talk Mode)",
"welcome-to-control-room": "Welcome. This is the control-room for the group-chat. There are different things you can use this room for:<br><br>\t<li>You can host a group chat with friends using a room. Share the blue link to invite guests who will join the chat automatically.</li>\t<li>A group room can handle around 4 to 30 guests, depending on numerous factors, including CPU and available bandwidth of all guests in the room.</li>\t<li>Solo-views of each video are offered under videos as they load. These can be used within an OBS Browser Source.</li>\t<li>You can use the auto-mixing Group Scene, the green link, to auto arrange multiple videos for you in OBS.</li>\t<li>You can use this control room to record isolated video or audio streams, but it is an experimental feature still.</li>\t<li>Videos in the Director's room will be of low quality on purpose; to save bandwidth/CPU</li>\t<li>Guest's in the room will see each other's videos at a very limited quality to conserve bandwidth/CPU.</li>\t<li>OBS will see a guest's video in high-quality; the default video bitrate is 2500kbps.</li>\t<br>\tAs guests join, their videos will appear below. You can bring their video streams into OBS as solo-scenes or you can add them to the Group Scene.\t<br>The Group Scene auto-mixes videos that have been added to the group scene. Please note that the Auto-Mixer requires guests be manually added to it for them to appear in it; they are not added automatically.<br><br>Apple mobile devices, such as iPhones and iPads, do not fully support Video Group Chat. This is a hardware constraint.<br><br>\tFor advanced options and parameters, <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">see the Wiki.</a>",
"more-than-four-can-join": "These four guest slots are just for demonstration. More than four guests can actually join a room.",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tWillkommen zu OBS.Ninja! Schicken Sie anderen Gästen eine Message.\n\t\t\t\t",
"names-and-labels-coming-soon": "\n\t\t\t\t\tNames identifying connected peers will be a feature in an upcoming release.\n\t\t\t\t",
"send-chat": "Abschicken",
"available-languages": "Verfügbare Sprachen:",
"add-more-here": "Weitere hinzufügen",
"waiting-for-camera-to-load": "waiting-for-camera-to-load",
"start": "START",
"share-your-mic": "Mikrofon teilen",
"share-your-camera": "Kamera teilen",
"share-your-screen": "Bildschirm teilen",
"join-room-with-mic": "Raum ohne Video betreten (nur mit Mikrofon)",
"share-screen-with-room": "Bildschirm mit Raum teilen",
"join-room-with-camera": "Raum mit Video betreten",
"click-start-to-join": "Start klicken",
"guests-only-see-director": "Gäste können nur das Gastgeber-Video sehen",
"default-codec-select": "Bevorzugter Video-Codec: ",
"obfuscate_url": "Einladungs-Link verschleiern",
"hide-the-links": " LINKS (Einladungslinks &amp; Szenen-Links)",
"invite-users-to-join": "Gäste können über diesen Link dem Raum beitreten",
"this-is-obs-browser-source-link": "In OBS verwenden, um den Gruppen-Videochat hinzuzufügen",
"mute-scene": "in Szene stummschalten",
"mute-guest": "Gast stummschalten",
"record-local": " lokal aufnehmen",
"record-remote": " remote aufnehmen",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Audio-Einstellungen",
"scenes-can-see-director": "Director will also be a performer",
"select-digital-effect": " Digital Video Effects: ",
"add-a-password": " Add a Password:",
"hide-guest": "hide guest",
"toggle-remote-speaker": "Deafen Guest",
"toggle-remote-display": "Blind Guest",
"force-keyframe": "Rainbow Puke",
"change-url": "Change URL",
"change-params": "URL Params",
"solo-video": "Highlight guest",
"stats-remote": " Scene Stats",
"apply-new-guest-settings": "Apply settings",
"cancel": "Cancel",
"add-to-calendar": "Add details to your Calendar:",
"no-effects-applied": "No effects applied",
"blurred-background": "Blurred background",
"digital-greenscreen": "Digital greenscreen",
"virtual-background": "Virtual background",
"use-chrome-instead": "Consider using a Chromium-based browser instead.<br>\n \t\t\t\t\t\tSafari is more prone to having audio issues",
"select-the-video-files-to-share": "SELECT THE VIDEO FILES TO SHARE",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"click-here-for-help": "Click Here for a quick overview and help",
"guests-hear-others": "Guests hear others",
"capture-a-group-scene": "CAPTURE A GROUP SCENE",
"auto-add-guests": "Auto-add guests",
"pro-audio-mode": "Pro-audio mode",
"hide-audio-only-sources": "Hide audio-only sources",
"ask-for-display-name": "Ask for display name",
"show-display-names": "Show display names",
"show-active-speaker": "Show active speakers",
"auto-select-microphone": "Auto-select default microphone",
"auto-select-camera": "Auto-select default camera",
"hide-setting-buttons": "Hide settings button",
"mini-self-preview": "Mini self-preview",
"virtual-backgrounds": "Virtual backgrounds",
"powerful-computers-only": "Only use with powerful computers and small groups!!",
"guests-see-HD-video": "Guests see HD video",
"no-self-preview": "Disable self-preview",
"raise-hand-button": "Display 'raise-hand' button",
"enable-compressor": "Enable audio compressor",
"enable-equalizer": "Enable equalizer as option",
"low-cpu=broadcast-codec": "Low-CPU broadcast codec",
"only-see-director-feed": "Only see the director's feed",
"mute-microphone-by-default": "Mute microphone by default",
"guest-joins-with-no-camera": "Guest joins with no camera",
"unmute-by-director-only": "Unmute by director only",
"obfuscate-link": "Obfuscate link and parameters",
"this-can-reduce-packet-loss": "This can reduce video corruption caused by packet loss",
"use-h264-codec": "Use H264 codec",
"show-active-speakers": "Show active speakers",
"force-mono-audio": "Force mono audio",
"learn-more-about-params": "Learn more about URL parameters at ",
"More-scene-options": "More scene options",
"additional-controls": "Additional controls",
"select-local-image": "Select Local Image",
"close-settings": "Close Settings",
"advanced": "Advanced ",
"invisible-guests": "Not Visible",
"add-to-google-calendar": "Add to Google Calendar",
"add-to-outlook-calendar": "Add to Outlook Calendar",
"add-to-yahoo-calendar": "Add to Yahoo Calendar",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"fade-videos-in": "Fade videos in",
"show-guest-tips": "Show guest setup tips",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"add-to-scene2": "add to scene 2",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast..",
"join-room": "Join room",
"waiting-for-mic-to-load": "Waiting for mic to load"
},
"placeholders": {
"join-by-room-name-here": "Raum über Namen betreten",
"enter-a-room-name-here": "Raumname eingeben",
"optional-room-password-here": "Raum-Passwort (optional)",
"give-this-media-source-a-name-optional-": "Namen für Quelle angeben (optional)",
"add-an-optional-password": "Passwort hinzufügen (optional)",
"enter-room-name-here": "Raumnamen eingeben",
"enter-chat-message-to-send-here": "Message",
"optional": "optional",
"enter-the-room-name-here": "Enter the room name here",
"enter-the-room-password-here": "Enter the room password here",
"enter-your-message-here": "Enter your message here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "Enter a new Display Name for this stream",
"submit-error-report": "Press OK to submit any error logs to VDO.Ninja. Error logs may contain private information.",
"director-redirect-1": "The director wishes to redirect you to the URL: ",
"director-redirect-2": "\n\nPress OK to be redirected.",
"add-a-label": "Add a label",
"audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. You can try claiming the room after the first director leaves.</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.",
"director": "Director",
"unknown-user": "Unknown User",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?",
"load-previous-session": "Would you like to load your previous session's settings?",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.",
"enter-display-name": "Please enter your display name:",
"enter-new-display-name": "Enter a new Display Name for this stream",
"what-bitrate": "What bitrate would you like to record at? (kbps)",
"enter-website": "Enter a website URL to share",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.",
"share-with-trusted": "Share only with those you trust",
"pass-recommended": "A password is recommended",
"insecure-room-name": "Insecure room name.",
"allowed-chars": "Allowed chars",
"transfer": "transfer",
"armed": "armed",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)",
"change-url": "change URL",
"mute-in-scene": "mute in scene",
"unmute-guest": "un-mute guest",
"undeafen": "un-deafen",
"deafen": "deafen guest",
"unblind": "un-blind",
"blind": "blind guest",
"unmute": "un-mute",
"mute-guest": "mute guest",
"unhide": "unhide guest",
"hide-guest": "hide guest",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

View File

@ -277,7 +277,7 @@
</head>
<body >
<div id="header" style="-webkit-app-region: drag;color:#6f6f6f;font-size:40px; line-height: 40px; padding: 20px; letter-spacing: 3; font-weight: bold;">Electron Capture</div>
<div id="header" style="-webkit-app-region: drag;color:#6f6f6f;font-size:40px; line-height: 40px; padding: 5px 10px; letter-spacing: 3; font-weight: bold;">Electron Capture</div>
<div class="container" >
<div id='warning4mac' style="display:none;"> ✨ Great News! OBS v26.1.2 <a href="https://github.com/obsproject/obs-browser/issues/209#issuecomment-748683083">now supports</a> VDO.Ninja without needing the Electron Capture app! 🥳</div>
@ -400,15 +400,16 @@ if ((location.hostname.toLowerCase() == "vdo.ninja") || (location.hostname.toLow
var checkVersion = setTimeout(function(){ // pre 1.5.2
compareVersions("0.0.0");
},500);
const ipcRenderer = require('electron').ipcRenderer;
console.log("ELECTRON DETECTED");
ipcRenderer.on('appVersion', function(event, version) {
clearTimeout(checkVersion);
console.log("version: "+version);
compareVersions(version);
})
ipcRenderer.send('getAppVersion');
try{
const ipcRenderer = require('electron').ipcRenderer;
console.log("ELECTRON DETECTED");
ipcRenderer.on('appVersion', function(event, version) {
clearTimeout(checkVersion);
console.log("version: "+version);
compareVersions(version);
})
ipcRenderer.send('getAppVersion');
} catch(e){}
}
}
} catch(e){console.error(e);}
@ -549,6 +550,20 @@ function modURL(){
var url = document.getElementById('changeText').value;
if (url.startsWith("obs.ninja")){
url = "https://"+url;
} else if (url.startsWith("youtube.com")){
url = "https://"+url;
} else if (url.startsWith("twitch.tv")){
url = "https://"+url;
} else if (url.startsWith("vdo.ninja")){
url = "https://"+url;
} else if (url.startsWith("http://")){
// pass
} else if (url.startsWith("https://")){
// pass
} else if (url.startsWith("file:")){
// pass
} else {
url = "https://"+url;
}
console.log(url);
return url;
@ -558,7 +573,7 @@ function gohere(){
localStorage.setItem('lastUrls', JSON.stringify(lastUrls));
var url = modURL();
if ((document.getElementById('changeText').value.includes("obs.ninja")) && (document.getElementById('changeText').value.includes("http")) && (document.getElementById('changeText').value.includes("&sink"))){
alert("Notice: OBS.Ninja has been replaced by VDO.Ninja.\n\nPlease update your links accordingly for audio output to work correctly.");
alert("Notice: OBS.Ninja has been renamed to VDO.Ninja.\n\nPlease update your links accordingly for audio output to work correctly.");
} else if (!(document.getElementById('changeText').value.includes(window.location.hostname)) && (document.getElementById('changeText').value.includes("http")) && (document.getElementById('changeText').value.includes("&sink"))){
alert("Notice: The &sink command is domain specific.\nVisit https://YOURDOMAIN.com/electron instead.");
}

394
en.json Normal file
View File

@ -0,0 +1,394 @@
{
"titles": {
"join-by-room-name-here": "Geben Sie einen Raumnamen ein",
"join-room": "Join room",
"load-the-next-guest-in-queue": "Load the next guest in queue",
"toggle-the-chat": "Toggle the Chat",
"mute-the-speaker": "Mute the Speaker",
"mute-the-mic": "Mute the Mic",
"disable-the-camera": "Disable the Camera",
"share-a-screen-with-others": "Share a Screen with others",
"create-a-secondary-stream": "Create a Secondary Stream",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"hangup-the-call": "Hangup the Call",
"alert-the-host-you-want-to-speak": "Alert the host you want to speak",
"record-your-stream-to-disk": "Record your stream to disk",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"cancel-the-director-s-video-audio": "Cancel the Director's Video/Audio",
"submit-any-error-logs": "Submit any error logs",
"show-help-info": "Show Help Info",
"language-options": "Language Options",
"add-to-calendar": "Add to Calendar",
"youtube-video-demoing-how-to-do-this": "Youtube Video demoing how to do this",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Invite a guest or camera source to publish into the group room",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If disabled, the invited guest will not be able to see or hear anyone in the room.",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Use this link in the OBS Browser Source to capture the video or audio",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If disabled, you must manually add a video to a scene for it to appear.",
"disables-echo-cancellation-and-improves-audio-quality": "Disables Echo Cancellation and improves audio quality",
"audio-only-sources-are-visually-hidden-from-scenes": "Audio-only sources are visually hidden from scenes",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"guest-will-be-prompted-to-enter-a-display-name": "Guest will be prompted to enter a Display Name",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Display Names will be shown in the bottom-left corner of videos",
"guests-not-actively-speaking-will-be-hidden": "Guests not actively speaking will be hidden",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "Request 1080p60 from the Guest instead of 720p60, if possible",
"the-default-microphone-will-be-pre-selected-for-the-guest": "The default microphone will be pre-selected for the guest",
"the-default-camera-device-will-selected-automatically": "The default camera device will selected automatically",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "The guest won't have access to changing camera settings or screenshare",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"increase-video-quality-that-guests-in-room-see-": "Increase video quality that guests in room see.",
"the-guest-will-not-see-their-own-self-preview-after-joining": "The guest will not see their own self-preview after joining",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Guests will have an option to poke the Director by pressing a button",
"add-an-audio-compressor-to-the-guest-s-microphone": "Add an audio compressor to the guest's microphone",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Add an Equalizer to the guest's microphone that the director can control",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-guest-can-only-see-the-director-s-video-if-provided": "The guest can only see the Director's video, if provided",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "The guest's microphone will be muted on joining. They can unmute themselves.",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Have the guest join muted, so only the director can Unmute the guest.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "The guest will not be asked for a video device on connection",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Make the invite URL encoded, so parameters are harder to tinker with by guests",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"move-the-user-to-another-room-controlled-by-another-director": "Move the user to another room, controlled by another director",
"send-a-direct-message-to-this-user-": "Send a Direct Message to this user.",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Force the user to Disconnect. They can always reconnect.",
"toggle-solo-voice-chat": "Toggle Solo Voice Chat",
"add-this-video-to-any-remote-scene-1-": "Add this Video to any remote '&scene=1'",
"mute-this-guest-everywhere": "Mute this guest everywhere",
"add-this-video-to-any-remote-scene-2-": "Add this Video to any remote '&scene=2'",
"remotely-mute-this-audio-in-all-remote-scene-views": "Remotely Mute this Audio in all remote '&scene' views",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"add-to-scene-8": "Add to Scene 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"solo-this-video-everywhere": "Solo this video everywhere",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"toggle-the-remote-guest-s-speaker-output": "Toggle the remote guest's speaker output",
"toggle-the-remote-guest-s-display-output": "Toggle the remote guest's display output",
"shift-this-video-down-in-order": "Shift this Video Down in Order",
"current-index-order-of-this-video": "Current Index Order of this Video",
"shift-this-video-up-in-order": "Shift this Video Up in Order",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Start Recording this remote stream to this local drive. *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "The Remote Guest will record their local stream to their local drive. *experimental*",
"remotely-change-the-volume-of-this-guest": "Remotely change the volume of this guest",
"disable-video-preview": "Disable Video Preview",
"low-quality-preview": "Low-Quality Preview",
"high-quality-preview": "High-Quality Preview",
"set-to-audio-channel-1": "Set to Audio Channel 1",
"set-to-audio-channel-2": "Set to Audio Channel 2",
"set-to-audio-channel-3": "Set to Audio Channel 3",
"set-to-audio-channel-4": "Set to Audio Channel 4",
"set-to-audio-channel-5": "Set to Audio Channel 5",
"set-to-audio-channel-6": "Set to Audio Channel 6",
"remote-audio-settings": "Remote Audio Settings",
"advanced-video-settings": "Advanced Video Settings",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"add-to-scene-2": "Add to Scene 2",
"activate-or-reload-this-video-device-": "Activate or Reload this video device.",
"tip-hold-ctrl-command-to-select-multiple": "tip: Hold CTRL (command) to select Multiple",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"cannot-see-videos": "Cannot see videos",
"cannot-hear-others": "Cannot hear others",
"see-director-only": "See director only",
"show-mini-preview": "Show Mini preview",
"raise-hand-button": "Raise hand button",
"show-labels": "Show labels",
"transfer-to-a-new-room": "Transfer to a new Room",
"enable-custom-password": "Enable custom password",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"add-group-chat-to-obs": "Add Group Chat to OBS",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "For large group rooms, this option can reduce the load on remote guests substantially",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "The director will be visible in scenes, as if a performer themselves.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Useful if you want to perform and direct at the same time",
"which-video-codec-would-you-want-used-by-default-": "Which video codec would you want used by default?",
"you-ll-enter-as-the-room-s-director": "You'll enter as the room's director",
"add-your-camera-to-obs": "Add your Camera to OBS",
"start-streaming": "start streaming",
"remote-screenshare-into-obs": "Remote Screenshare into OBS",
"create-reusable-invite": "Create Reusable Invite",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Ideal for 1080p60 gaming, if your computer and upload are up for it",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Better video compression and quality at the cost of increased CPU encoding load",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Disable digital audio-effects and increase audio bitrate",
"the-guest-will-not-have-a-choice-over-audio-options": "The guest will not have a choice over audio-options",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "The guest will only be able to select their webcam as an option",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Hold CTRL and the mouse wheel to zoom in and out remotely of compatible video streams",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "Encode the URL so that it's harder for a guest to modify the settings.",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Add a password to make the stream inaccessible to those without the password",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Add the guest to a group-chat room; it will be created automatically if needed.",
"customize-the-room-settings-for-this-guest": "Customize the room settings for this guest",
"more-options": "More Options",
"hold-ctrl-or-cmd-to-select-multiple-files": "Hold CTRL (or CMD) to select multiple files",
"enter-an-https-url": "Enter an HTTPS URL",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees"
},
"innerHTML": {
"copy-this-url": "Copy this URL into an OBS \"Browser Source\"",
"you-are-in-the-control-center": "Control center for room:",
"joining-room": "You are in room",
"push-to-talk-enable": " enable director`s microphone or video<br>(only guests can see this feed)",
"hide-the-links": " LINKS (GUEST INVITES &amp; SCENES)",
"click-for-quick-room-overview": "\n\t\t\t\t\t\t<i class=\"las la-question-circle\"></i> <span data-translate=\"click-here-for-help\">Click Here for a quick overview and help</span>\n\t\t\t\t\t",
"click-here-for-help": "Click Here for a quick overview and help",
"welcome-to-control-room": "\n\t\t\t\t\t\t<b>Welcome. This is the director's control-room for the group-chat.</b><br><br>\n\t\t\t\t\t\tYou can host a group chat with friends using a room. Share the blue link to invite guests who will join the chat automatically.\n\t\t\t\t\t\t<br><br>\n\t\t\t\t\t\t<font style=\"color:red\">Known Limitations with Group Rooms:</font><br>\n\t\t\t\t\t\t<li>A group room can handle up to around 30 guests, depending on numerous factors, including CPU and available bandwidth of all guests in the room. To achieve more than around 7-guests though, you will likely want to <a href=\"https://www.youtube.com/watch?v=bpRa8-UYCGc\" title=\"Youtube Video demoing how to do this\">disable video sharing between guests</a>. Using &amp;broadcast, &amp;roombitrate=0 or &amp;novideo are options there.</li>\n\t\t\t\t\t\t\n\t\t\t\t\t\t<li>Videos will appear of low quality on purpose for guests and director; this is to save bandwidth and CPU resources. It will be high-quality within OBS still though.</li>\n\t\t\t\t\t\t\n\t\t\t\t\t\t<li>The state of the scenes, such as which videos are active in a scene, are lost when the director resets the control-room or the scene.</li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\tFurther Notes:<br><br>\n\t\t\t\t\t\t<li>Links to Solo-views of each guest video are offered under videos as they load. These can be used within an OBS Browser Source.</li>\n\t\t\t\t\t\t<li>You can use the auto-mixing Group Scenes, the green links, to auto arrange multiple videos for you in OBS.</li>\n\t\t\t\t\t\t<li>You can use this control room to record isolated video or audio streams, but it is an experimental feature still.</li>\n\t\t\t\t\t\t<li>If you transfer a guest from one room to another, they won't know which room they have been transferred to.</li>\n\t\t\t\t\t\t<li>OBS will see a guest's video in high-quality; the default video bitrate is 2500kbps. Setting higher bitrates will improve motion.</li>\n\t\t\t\t\t\t<li>VP8 is typically the default video codec, but using &amp;codec=vp9 or &amp;codec=h264 as a URL in OBS can help to reduce corrupted video puke issues.</li>\n\t\t\t\t\t\t<li>&amp;stereo=2 can be added to guests to turn off audio effects, such as echo cancellation and noise-reduction.</li>\n\t\t\t\t\t\t<li>https://invite.cam is a free service provided that can help obfuscuate the URL parameters of an invite link given to guests.</li>\n\t\t\t\t\t\t<li>Adding &amp;showonly=SOME_OBS_VIRTUALCAM to the guest invite links allows for only a single video to be seen by the guests; this can be output of the OBS Virtual Camera for example</li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\n\t\t\t\t\t\tFor advanced URL options and parameters, <a href=\"https://docs.vdo.ninja/advanced-settings\">see the Wiki.</a>\n\t\t\t\t\t",
"invite-users-to-join": "Guests can use the link to join the group room",
"guests-hear-others": "Guests hear others",
"capture-a-group-scene": "CAPTURE A GROUP SCENE",
"this-is-obs-browser-source-link": "Use in OBS or other studio software to capture the group video mix",
"auto-add-guests": "Auto-add guests",
"pro-audio-mode": "Pro-audio mode",
"hide-audio-only-sources": "Hide audio-only sources",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"ask-for-display-name": "Ask for display name",
"show-display-names": "Show display names",
"show-active-speaker": "Show active speakers",
"auto-select-microphone": "Auto-select default microphone",
"auto-select-camera": "Auto-select default camera",
"hide-setting-buttons": "Hide settings button",
"mini-self-preview": "Mini self-preview",
"virtual-backgrounds": "Virtual backgrounds",
"fade-videos-in": "Fade videos in",
"powerful-computers-only": "Only use with powerful computers and small groups!!",
"guests-see-HD-video": "Guests see HD video",
"no-self-preview": "Disable self-preview",
"raise-hand-button": "Display 'raise-hand' button",
"enable-compressor": "Enable audio compressor",
"enable-equalizer": "Enable equalizer as option",
"show-guest-tips": "Show guest setup tips",
"low-cpu=broadcast-codec": "Low-CPU broadcast codec",
"only-see-director-feed": "Only see the director's feed",
"mute-microphone-by-default": "Muted; guest can unmute",
"unmute-by-director-only": "Muted; director can unmute",
"guest-joins-with-no-camera": "Guest joins with no camera",
"obfuscate-link": "Obfuscate link and parameters",
"this-can-reduce-packet-loss": "Can reduce packet loss video corruption in OBS on PC",
"use-h264-codec": "Use H264 codec",
"show-active-speakers": "Show active speakers",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"force-mono-audio": "Force mono audio",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"learn-more-about-params": "Learn more about URL parameters at ",
"forward-to-room": "Transfer",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Message",
"disconnect-guest": "Hangup",
"voice-chat": "<i class=\"las la-microphone\" style=\"color:#090\"></i> Solo Talk",
"add-to-scene": "add to scene 1",
"mute-guest": "mute guest",
"More-scene-options": "More scene options",
"add-to-scene2": "add to scene 2",
"mute-scene": "mute in scene",
"force-keyframe": "Rainbow Puke Fix",
"stats-remote": " Scene Stats",
"additional-controls": "Additional controls",
"solo-video": "Highlight guest",
"hide-guest": "hide guest",
"toggle-remote-speaker": "Deafen Guest",
"toggle-remote-display": "Blind Guest",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"change-url": "Change URL",
"change-params": "URL Params",
"record-local": " Record Local",
"record-remote": " Record Remote",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Audio Settings",
"advanced-camera-settings": "<i class=\"las la-sliders-h\"></i> Video Settings",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"video-source": " Video Source ",
"max-resolution": "Max Resolution",
"balanced": "Balanced",
"smooth-cool": "Smooth and Cool",
"select-audio-source": " Audio Source(s) ",
"select-output-source": " Audio Output Destination: ",
"select-digital-effect": " Digital Video Effects: ",
"no-effects-applied": "No effects applied",
"blurred-background": "Blurred background",
"digital-greenscreen": "Digital greenscreen",
"virtual-background": "Virtual background",
"select-local-image": "Select Local Image",
"close-settings": "Close Settings",
"advanced": "Advanced ",
"open-in-new-tab": "Open in new Tab",
"copy-to-clipboard": "Copy to Clipboard",
"send-chat": "Send",
"apply-new-guest-settings": "Apply settings",
"cancel": "Cancel",
"invisible-guests": "Not Visible",
"available-languages": "Available Languages:",
"add-more-here": "Add More Here!",
"add-to-calendar": "Add details to your Calendar:",
"add-to-google-calendar": "Add to Google Calendar",
"add-to-outlook-calendar": "Add to Outlook Calendar",
"add-to-yahoo-calendar": "Add to Yahoo Calendar",
"logo-header": "\n\t\t\t\t\t<font id=\"qos\">V</font>DO.Ninja \n\t\t\t\t",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"add-group-chat": "Create a Room",
"rooms-allow-for": "Rooms allow for group-chat and the tools to manage multiple guests.",
"room-name": "Room Name",
"password-input-field": "Password",
"guests-only-see-director": "Guests can only see the Director's Video",
"scenes-can-see-director": "Director will also be a performer",
"default-codec-select": "Preferred Video Codec: ",
"enter-the-rooms-control": "Enter the Room's Control Center",
"show-tips": "Show me some tips..",
"added-notes": "\n\t\t\t\t\t\t\t\t<u>\n\t\t\t\t\t\t\t\t\t<i>Important Tips:</i><br><br>\n\t\t\t\t\t\t\t\t</u>\n\t\t\t\t\t\t\t\t<li>Disabling video sharing between guests will improve performance</li>\n\t\t\t\t\t\t\t\t<li>Invite only guests to the room that you trust.</li>\n\t\t\t\t\t\t\t\t<li>The \"Recording\" option is considered experimental.</li>\n\t\t\t\t\t\t\t\t<li><a href=\"https://params.vdo.ninja\" style=\"color:black;\"><u>Advanced URL parameters</u></a> are available to customize rooms.</li>\n\t\t\t\t\t\t\t",
"back": "Back",
"add-your-camera": "Add your Camera to OBS",
"ask-for-permissions": "Allow Access to Camera/Microphone",
"waiting-for-camera": "Waiting for Camera to Load",
"no-audio": "No Audio",
"add-a-password": " Add a Password:",
"use-chrome-instead": "Consider using a Chromium-based browser instead.<br>\n \t\t\t\t\t\tSafari is more prone to having audio issues",
"remote-screenshare-obs": "Remote Screenshare into OBS",
"select-screen-to-share": "SELECT SCREEN TO SHARE",
"audio-sources": "Audio Sources",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"create-reusable-invite": "Create Reusable Invite",
"here-you-can-pre-generate": "Here you can pre-generate a reusable Browser Source link and a related guest invite link.",
"generate-invite-link": "GENERATE THE INVITE LINK",
"advanced-paramaters": "Advanced Options",
"unlock-video-bitrate": "Unlock Video Bitrate (20mbps)",
"force-vp9-video-codec": "Force VP9 Video Codec",
"enable-stereo-and-pro": "Enable Stereo and Pro HD Audio",
"video-resolution": "Video Resolution: ",
"hide-mic-selection": "Force Default Microphone",
"hide-screen-share": "Hide Screenshare Option",
"allow-remote-control": "Remote Control Camera Zoom (android)",
"obfuscate_url": "Obfuscate the Invite URL",
"add-a-password-to-stream": " Add a password:",
"add-the-guest-to-a-room": " Add the guest to a room:",
"invite-group-chat-type": "This room guest can:",
"can-see-and-hear": "Can see and hear the group chat",
"can-hear-only": "Can only hear the group chat",
"cant-see-or-hear": "Cannot hear or see the group chat",
"share-local-video-file": "Stream Media File",
"select-the-video-files-to-share": "SELECT THE VIDEO FILES TO SHARE",
"share-website-iframe": "Share Website",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"run-a-speed-test": "Run a Speed Test",
"read-the-guides": "Browse the Guides",
"info-blob": "\n\t\t\t\t\t\t\t<h2>What is VDO.Ninja</h2>\n\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t<li>100% <b>free</b>; no downloads; no personal data collection; no sign-in</li>\n\t\t\t\t\t\t\t<li>Bring live video from your smartphone, remote computer, or friends directly into OBS or other studio software.</li>\n\t\t\t\t\t\t\t<li>We use cutting edge Peer-to-Peer forwarding technology that offers privacy and ultra-low latency</li>\n\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t<li>Youtube video \n\t\t\t\t\t\t\t\t<i class=\"lab la-youtube\"></i>\n\t\t\t\t\t\t\t\t<a href=\"https://www.youtube.com/watch?v=vLpRzMjUDaE&amp;list=PLWodc2tCfAH1WHjl4WAOOoRSscJ8CHACe&amp;index=2\" alt=\"Youtube video demoing VDO.Ninja\">Demoing it here</a>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t<i>\n\t\t\t\t\t\t\t\t<font style=\"color: red;\">Known issues:</font>\n\t\t\t\t\t\t\t</i>\n\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\tSome devices that use H264 hardware encoding can experience video glitching; switching to VP8 or VP9 as a codec can help.\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\tIf using multiple group scenes at a time, iOS devices may fail to work if the hardware encoders max out. Perhaps try VP8 as a codec instead.\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\tA list of less common issues can <a href=\"https://docs.vdo.ninja/common-errors-and-known-issues/known-issues-browser-bugs-and-more\">be found here</a>.\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t<h4 style=\"color:#daad09;\">\n\t\t\t\t\t\t\t\t👋 👀 Welcome to VDO Ninja! We've rebranded! 📼 Nothing else is changing and we're staying 100% free.\n\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t🌻 Site Updated on July 8th. The <a href=\"https://docs.vdo.ninja/release-notes/v18_3\">v18.3 release notes are here</a>. If new issues occur, the previous version can also be <a href=\"https://vdo.ninja/v18/\">found here</a>.\n\n\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t<h3>\n\t\t\t\t\t\t\t 🛠 For support, see the <a href=\"https://www.reddit.com/r/VDONinja/\">sub-reddit <i class=\"lab la-reddit-alien\"></i></a> or join the <a href=\"https://discord.gg/T4xpQVv\">Discord <i class=\"lab la-discord\"></i></a>. The <a href=\"https://docs.vdo.ninja/\">documentation is here</a> and my personal email is <i>steve@seguin.email</i>\n\t\t\t\t\t\t\t</h3> \n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"more-than-four-can-join": "These four guest slots are just for demonstration. More than four guests can actually join a room.",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tWelcome to VDO.Ninja! You can send text messages directly to connected peers from here.\n\t\t\t\t",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast.."
},
"placeholders": {
"join-by-room-name-here": "Join by Room Name here",
"enter-your-message-here": "Enter your message here",
"enter-chat-message-to-send-here": "Enter chat message to send here",
"enter-the-room-name-here": "Enter the room name here",
"enter-the-room-password-here": "Enter the room password here",
"enter-a-room-name-here": "Enter a Room Name here",
"optional-room-password-here": "Optional room password here",
"optional": "optional",
"give-this-media-source-a-name-optional-": "Give this media source a name (optional)",
"add-an-optional-password": "Add an optional password",
"enter-room-name-here": "Enter Room name here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "Enter a new Display Name for this stream",
"submit-error-report": "Press OK to submit any error logs to VDO.Ninja. Error logs may contain private information.",
"director-redirect-1": "The director wishes to redirect you to the URL: ",
"director-redirect-2": "\n\nPress OK to be redirected.",
"add-a-label": "Add a label",
"audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. You can try claiming the room after the first director leaves.</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.",
"director": "Director",
"unknown-user": "Unknown User",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?",
"load-previous-session": "Would you like to load your previous session's settings?",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.",
"enter-display-name": "Please enter your display name:",
"enter-new-display-name": "Enter a new Display Name for this stream",
"what-bitrate": "What bitrate would you like to record at? (kbps)",
"enter-website": "Enter a website URL to share",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.",
"share-with-trusted": "Share only with those you trust",
"pass-recommended": "A password is recommended",
"insecure-room-name": "Insecure room name.",
"allowed-chars": "Allowed chars",
"transfer": "transfer",
"armed": "armed",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)",
"change-url": "change URL",
"mute-in-scene": "mute in scene",
"unmute-guest": "un-mute guest",
"undeafen": "un-deafen",
"deafen": "deafen guest",
"unblind": "un-blind",
"blind": "blind guest",
"unmute": "un-mute",
"mute-guest": "mute guest",
"unhide": "unhide guest",
"hide-guest": "hide guest",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

406
es.json Normal file
View File

@ -0,0 +1,406 @@
{
"titles": {
"join-by-room-name-here": "Introduce un nombre de sala para entrar",
"join-room": "Unirse sala",
"load-the-next-guest-in-queue": "Cargar el siguiente invitado en la cola",
"toggle-the-chat": "Conmutar Chat",
"mute-the-speaker": "Mute Orador",
"mute-the-mic": "Mute Micro",
"disable-the-camera": "Desactivar Cámara",
"share-a-screen-with-others": "Compartir Pantalla con otros",
"create-a-secondary-stream": "Crear un Stream Secundario",
"settings": "Configuración",
"hangup-the-call": "Colgar",
"alert-the-host-you-want-to-speak": "Avisar Anfitrión que quieres hablar",
"record-your-stream-to-disk": "Grabar tu stream a disco",
"cancel-the-director-s-video-audio": "Cancelar el Video/Audio del Director",
"submit-any-error-logs": "Enviar cualquier registro de error",
"show-help-info": "Mostrar Ayuda",
"language-options": "Opciones Idioma",
"add-to-calendar": "Añadir al Calendario",
"add-group-chat-to-obs": "Añadir Chat Grupal",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "Para salas de grupos grandes, esta opción reduce la carga en los clientes remotos de manera substancial",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "El director será visible en las escenas, como si fuese a actuar.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Útil si quieres actuar y dirigir al mismo tiempo",
"which-video-codec-would-you-want-used-by-default-": "¿Qué codec de video quieres se utilice por defecto?",
"you-ll-enter-as-the-room-s-director": "Nunca entrarás como director de sala",
"add-your-camera-to-obs": "Añadir Cámara",
"start-streaming": "iniciar streaming",
"tip-hold-ctrl-command-to-select-multiple": "truco: Mantén pulsado CTRL (o CMD) para selección Múltiple",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"remote-screenshare-into-obs": "Remote Screenshare",
"create-reusable-invite": "Create Reusable Invite",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Ideal para juegos 1080p60, si tu PC y conexión lo permiten",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Mejor compresión y calidad del video pese al incremento de carga en la CPU",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Deshabilitar los efectos de sonido digital e incrementar el bitrate audio",
"the-guest-will-not-have-a-choice-over-audio-options": "El invitado no tendrá opciones de audio a escoger",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "El invitado sólo podrá seleccionar su opción de cámara",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Mantén CTRL y la rueda del ratón para hacer zoom en los videos",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "Encode the URL so that it's harder for a guest to modify the settings.",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Añade un password para evitar el acceso al video sin la clave",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Añade al invitado a una sala de chat grupal; se creará automáticamente en caso necesario.",
"customize-the-room-settings-for-this-guest": "Customizar la configuración de sala para este invitado",
"more-options": "Más Opciones",
"hold-ctrl-or-cmd-to-select-multiple-files": "Mantener CTRL (o CMD) para seleccionar varios ficheros",
"enter-an-https-url": "Introduce una URL HTTPS",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"youtube-video-demoing-how-to-do-this": "Youtube Video demoing how to do this",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Invite a guest or camera source to publish into the group room",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If disabled, the invited guest will not be able to see or hear anyone in the room.",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Use this link as a browser source in your Studio software to capture the video or audio",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If disabled, you must manually add a video to a scene for it to appear.",
"disables-echo-cancellation-and-improves-audio-quality": "Disables Echo Cancellation and improves audio quality",
"audio-only-sources-are-visually-hidden-from-scenes": "Audio-only sources are visually hidden from scenes",
"guest-will-be-prompted-to-enter-a-display-name": "Guest will be prompted to enter a Display Name",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Display Names will be shown in the bottom-left corner of videos",
"guests-not-actively-speaking-will-be-hidden": "Guests not actively speaking will be hidden",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "Request 1080p60 from the Guest instead of 720p60, if possible",
"the-default-microphone-will-be-pre-selected-for-the-guest": "The default microphone will be pre-selected for the guest",
"the-default-camera-device-will-selected-automatically": "The default camera device will selected automatically",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "The guest won't have access to changing camera settings or screenshare",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"increase-video-quality-that-guests-in-room-see-": "Increase video quality that guests in room see.",
"the-guest-will-not-see-their-own-self-preview-after-joining": "The guest will not see their own self-preview after joining",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Guests will have an option to poke the Director by pressing a button",
"add-an-audio-compressor-to-the-guest-s-microphone": "Add an audio compressor to the guest's microphone",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Add an Equalizer to the guest's microphone that the director can control",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-guest-can-only-see-the-director-s-video-if-provided": "The guest can only see the Director's video, if provided",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "The guest's microphone will be muted on joining. They can unmute themselves.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "The guest will not be asked for a video device on connection",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Have the guest join muted, so only the director can Unmute the guest.",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Make the invite URL encoded, so parameters are harder to tinker with by guests",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"move-the-user-to-another-room-controlled-by-another-director": "Move the user to another room, controlled by another director",
"send-a-direct-message-to-this-user-": "Send a Direct Message to this user.",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Force the user to Disconnect. They can always reconnect.",
"toggle-solo-voice-chat": "Toggle Solo Voice Chat",
"add-this-video-to-any-remote-scene-1-": "Añadir este Video a '&scene=1' remoto",
"mute-this-guest-everywhere": "Mute this guest everywhere",
"add-this-video-to-any-remote-scene-2-": "Añadir este Video a '&scene=2' remoto",
"remotely-mute-this-audio-in-all-remote-scene-views": "Remotely Mute this Audio in all remote '&scene' views",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"add-to-scene-8": "Add to Scene 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"solo-this-video-everywhere": "Solo this video everywhere",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"toggle-the-remote-guest-s-speaker-output": "Toggle the remote guest's speaker output",
"toggle-the-remote-guest-s-display-output": "Toggle the remote guest's display output",
"shift-this-video-down-in-order": "Shift this Video Down in Order",
"current-index-order-of-this-video": "Current Index Order of this Video",
"shift-this-video-up-in-order": "Shift this Video Up in Order",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Comenzar Grabación de este stream remoto en local. *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "The Remote Guest will record their local stream to their local drive. *experimental*",
"remotely-change-the-volume-of-this-guest": "Remotely change the volume of this guest",
"disable-video-preview": "Deshabilitar Previsualización",
"low-quality-preview": "Previo Baja Calidad",
"high-quality-preview": "Previo Alta Calidad",
"set-to-audio-channel-1": "Activar Canal Audio 1",
"set-to-audio-channel-2": "Activar Canal Audio 2",
"set-to-audio-channel-3": "Activar Canal Audio 3",
"set-to-audio-channel-4": "Activar Canal Audio 4",
"set-to-audio-channel-5": "Activar Canal Audio 5",
"set-to-audio-channel-6": "Activar Canal Audio 6",
"remote-audio-settings": "Configuración Audio Remoto",
"advanced-video-settings": "Configuración Video Avanzada",
"add-to-scene-2": "Añadir a Escena 2",
"activate-or-reload-this-video-device-": "Activate or Reload this video device.",
"cannot-see-videos": "Cannot see videos",
"cannot-hear-others": "Cannot hear others",
"see-director-only": "See director only",
"show-mini-preview": "Show Mini preview",
"raise-hand-button": "Raise hand button",
"show-labels": "Mostrar Rótulos",
"transfer-to-a-new-room": "Transferir a nueva Sala",
"enable-custom-password": "Activar password personalizado",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees",
"enter-a-room-name-to-quick-join": "Enter a room name to quick join"
},
"innerHTML": {
"copy-this-url": "Copia esta URL como fuente \"Navegador\" de OBS",
"you-are-in-the-control-center": "Estás en la sala del panel de control",
"joining-room": "Estás entrado en la sala",
"add-group-chat": "Agregar grupo de chat a OBS",
"rooms-allow-for": "Las salas permiten un chat grupal simplificado y la administración avanzada de múltiples transmisiones a la vez.",
"room-name": "Nombre de sala",
"password-input-field": "Password",
"guests-only-see-director": "Guests can only see the Director's Video",
"scenes-can-see-director": "Director will also be a performer",
"default-codec-select": "Preferred Video Codec: ",
"enter-the-rooms-control": "Entrar al panel de control",
"show-tips": "Muéstrame algunos consejos.",
"added-notes": "\n\t\t\t\t\t\t\t\t<u>\n\t\t\t\t\t\t\t\t\t<i>Consejos Importantes:</i><br><br>\n\t\t\t\t\t\t\t\t</u>\n\t\t\t\t\t\t\t\t<li>Deshabilitar el video compartido entre invitados mejora el rendimiento</li>\n\t\t\t\t\t\t\t\t<li>Invite only guests to the room that you trust.</li>\n\t\t\t\t\t\t\t\t<li>La opción \"Recording\" se considera experimental.</li>",
"back": "Atrás",
"add-your-camera": "Agregar tu camara a OBS",
"ask-for-permissions": "Allow Access to Camera/Microphone",
"waiting-for-camera": "Esperando a que se cargue la cámara",
"video-source": "Fuente de video",
"max-resolution": "Max. Resolución",
"balanced": "Equilibrado",
"smooth-cool": "Fluido",
"select-audio-source": "Seleccionar fuentes de audio",
"no-audio": "Sin Audio",
"select-output-source": " Destino de la salida de audio: ",
"select-digital-effect": " Efectos Video Digital: ",
"no-effects-applied": "Sin efectos aplicados",
"blurred-background": "Fondo Difuminado",
"digital-greenscreen": "Pantalla Verde Digital",
"virtual-background": "Fondo Virtual",
"add-a-password": " Añadir un Password:",
"use-chrome-instead": "Recomendamos utilizar un navegador basado en Chromium.<br>\n \t\t\t\t\t\tSafari puede sufrir problemas de audio",
"remote-screenshare-obs": "Compartir pantalla",
"select-screen-to-share": "SELECCIONAR PANTALLA PARA COMPARTIR",
"audio-sources": "Fuentes de Audio",
"create-reusable-invite": "Crear una invitación reutilizable",
"here-you-can-pre-generate": "Aquí puedes generar un enlace reutilizable como fuente del navegador e invitados.",
"generate-invite-link": "GENERAR EL LINK DE INVITACIÓN",
"advanced-paramaters": "Parámetros Avanzados",
"unlock-video-bitrate": "Desbloquear Video Bitrate (20mbps)",
"force-vp9-video-codec": "Forzar VP9 Video Codec (menos artefactos)",
"enable-stereo-and-pro": "Habilitar Estéreo y Pro Audio HD",
"video-resolution": "Resolución de vídeo: ",
"hide-mic-selection": "Force Default Microphone",
"hide-screen-share": "Ocultar opción compartir pantalla",
"allow-remote-control": "Control remoto del zoom de la cámara (android)",
"obfuscate_url": "Obfuscar la URL Invitación",
"add-a-password-to-stream": " Añadir password:",
"add-the-guest-to-a-room": " Añadir invitado a una sala:",
"invite-group-chat-type": "Este invitado de la sala puede:",
"can-see-and-hear": "Puede ver y oir el chat de grupo",
"can-hear-only": "Sólo puede oir el chat de grupo",
"cant-see-or-hear": "No puede oir ni ver el chat de grupo",
"share-local-video-file": "Stream Fichero Multimedia",
"select-the-video-files-to-share": "SELECCIONA EL FICHERO DE VIDEO A COMPARTIR",
"share-website-iframe": "Comparte Sitio Web",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"run-a-speed-test": "Run a Speed Test",
"read-the-guides": "Browse the Guides",
"info-blob": "",
"hide-the-links": " ENLACES (INVITACIONES &amp; ESCENAS)",
"click-for-quick-room-overview": "\n\t\t\t\t\t\t<i class=\"las la-question-circle\"></i> <span data-translate=\"click-here-for-help\">Pulsar aquí para un resumen rápido y ayuda</span>\n\t\t\t\t\t",
"click-here-for-help": "Pulsar aquí para un resumen rápido y ayuda",
"welcome-to-control-room": "\n\t\t\t\t\t\t<b>Bienvenido. Esta es la sala de control del director para el grupo de chat.</b><br><br>\n\t\t\t\t\t\tPuedes organizar un grupo de chat con amigos utilizando una sala. Comparte el enlace azul con los invitados para que puedan entrar directamente al chat.\n\t\t\t\t\t\t<br><br>\n\t\t\t\t\t\t<font style=\"color:red\">Limitaciones conocidas de las Salas:</font><br>\n\t\t\t\t\t\t<li>Una sala grupal puede gestionar hasta unos 30 invitados, dependiendo de varios factores, incluyendo CPU y ancho de banda disponible en todos los invitados en la sala.</li>\n\t\t\t\t\t\t\n\t\t\t\t\t\t<li>El Video aparece en baja calidad a propósito en los invitados y director; esto es para reducir los recursos de ancho de banda y CPU.</li>",
"invite-users-to-join": "Los Invitados pueden utilizar el enlace para unirse sala grupal",
"guests-hear-others": "Invitados escuchan a otros",
"capture-a-group-scene": "CAPTURA UNA ESCENA DE GRUPO",
"this-is-obs-browser-source-link": "Utilizar en OBS u otro software para capturar la mezcla de video grupal",
"auto-add-guests": "Auto añadir invitados",
"pro-audio-mode": "Modo Pro-audio",
"hide-audio-only-sources": "Ocultar fuentes de sólo audio",
"ask-for-display-name": "Preguntar nombre a mostrar",
"show-display-names": "Mostrar nombres",
"show-active-speaker": "Mostrar orador activo",
"auto-select-microphone": "Auto-selección micro por defecto",
"auto-select-camera": "Auto-selección cámara por defecto",
"hide-setting-buttons": "Ocultar botón configuración",
"mini-self-preview": "Mini visor propio",
"virtual-backgrounds": "Fondo virtual",
"powerful-computers-only": "¡Sólo utilizar con PC potente y grupo pequeño!",
"guests-see-HD-video": "Invitados ven video HD",
"no-self-preview": "Desactivar visualización propia",
"raise-hand-button": "Mostrar botón de 'Levantar-mano'",
"enable-compressor": "Activar compresor de audio",
"enable-equalizer": "Activar ecualizador como opción",
"low-cpu=broadcast-codec": "Codec emisión de bajo consumo CPU",
"only-see-director-feed": "Sólo ver la fuente del director",
"mute-microphone-by-default": "Mute del micro por defecto",
"guest-joins-with-no-camera": "Invitados entran sin cámara",
"unmute-by-director-only": "Quitar mute sólo director",
"obfuscate-link": "Ofuscar enlace y parámetros",
"this-can-reduce-packet-loss": "Esto puede reducir la corrupción de video causada por pérdida de paquetes",
"use-h264-codec": "Usar codec H264",
"show-active-speakers": "Mostar orador activo",
"force-mono-audio": "Forzar audio mono",
"learn-more-about-params": "Aprende sobre los parámetros URL en ",
"more-than-four-can-join": "Estos espacios para invitados son sólo un ejemplo. Pueden unirse más de cuatro invitados.",
"forward-to-room": "Enviar",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Mensaje",
"disconnect-guest": "Colgar",
"voice-chat": "<i class=\"las la-microphone\" style=\"color:#090\"></i> Hablar Solo",
"add-to-scene": "añadir a escena 1",
"mute-guest": "mute invitado",
"More-scene-options": "Más opciones de escena",
"mute-scene": "mute en escena",
"force-keyframe": "Reparar Arcoiris",
"stats-remote": " Valores Escena",
"additional-controls": "Controles Adicionales",
"solo-video": "Resaltar invitado",
"hide-guest": "ocultar invitado",
"toggle-remote-speaker": "Mutear Invitado",
"toggle-remote-display": "Cegar Invitado",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"change-url": "Cambiar URL",
"change-params": "Param. URL",
"record-local": " Grabación Local",
"record-remote": " Grabación Remota",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Configuración Audio",
"advanced-camera-settings": "<i class=\"las la-sliders-h\"></i> Configuración Video",
"select-local-image": "Selecciona una Imagen local",
"close-settings": "Cerrar Configuración",
"advanced": "Avanzado ",
"open-in-new-tab": "Abrir en una pestaña nueva",
"copy-to-clipboard": "Copia al portapapeles",
"send-chat": "Enviar",
"apply-new-guest-settings": "Aplicar configuración",
"cancel": "Cancelar",
"invisible-guests": "No Visible",
"available-languages": "Idiomas Disponibles:",
"add-more-here": "¡Añade más Aquí!",
"add-to-calendar": "Añadir detalles a tu Calendario:",
"add-to-google-calendar": "Añadir a Calendario Google",
"add-to-outlook-calendar": "Añadir a Calendario Outlook",
"add-to-yahoo-calendar": "Añadir a Calendario Yahoo",
"push-to-talk-enable": " enable director`s microphone or video<br>(only guests can see this feed)",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"fade-videos-in": "Fade videos in",
"show-guest-tips": "Show guest setup tips",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"add-to-scene2": "add to scene 2",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"logo-header": "\n\t\t\t\t\t<font id=\"qos\">V</font>DO.Ninja \n\t\t\t\t",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tWelcome to VDO.Ninja! You can send text messages directly to connected peers from here.\n\t\t\t\t",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast..",
"join-room": "Join room",
"join-room-with-mic": "Join room with Microphone",
"join-room-with-camera": "Join Room with Camera",
"share-screen-with-room": "Screenshare with Room",
"share-your-mic": "Share your Microphone",
"share-your-camera": "Share your Camera",
"share-your-screen": "Share your Screen",
"click-start-to-join": "Click Start to Join",
"waiting-for-mic-to-load": "Waiting for mic to load",
"waiting-for-camera-to-load": "Waiting for Camera to load"
},
"placeholders": {
"join-by-room-name-here": "Unirse por Nombre de Sala aquí",
"enter-a-room-name-here": "Introduce un Nombre de Sala aquí",
"optional-room-password-here": "Password de sala opcional aquí",
"give-this-media-source-a-name-optional-": "Dar a la fuente de nedios un nombre (opcional)",
"add-an-optional-password": "Añadir una password opcional",
"enter-room-name-here": "Introduce el nombre de Sala aquí",
"enter-chat-message-to-send-here": "Introduce el mensaje de chat a enviar aquí",
"optional": "opcional",
"enter-the-room-name-here": "Introduce el nombre de Sala aquí",
"enter-the-room-password-here": "Introduce el password de la Sala aquí",
"enter-your-message-here": "Enter your message here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "Enter a new Display Name for this stream",
"submit-error-report": "Press OK to submit any error logs to VDO.Ninja. Error logs may contain private information.",
"director-redirect-1": "The director wishes to redirect you to the URL: ",
"director-redirect-2": "\n\nPress OK to be redirected.",
"add-a-label": "Add a label",
"audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. You can try claiming the room after the first director leaves.</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.",
"director": "Director",
"unknown-user": "Unknown User",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?",
"load-previous-session": "Would you like to load your previous session's settings?",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.",
"enter-display-name": "Please enter your display name:",
"enter-new-display-name": "Enter a new Display Name for this stream",
"what-bitrate": "What bitrate would you like to record at? (kbps)",
"enter-website": "Enter a website URL to share",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.",
"share-with-trusted": "Share only with those you trust",
"pass-recommended": "A password is recommended",
"insecure-room-name": "Insecure room name.",
"allowed-chars": "Allowed chars",
"transfer": "transfer",
"armed": "armed",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)",
"change-url": "change URL",
"mute-in-scene": "mute in scene",
"unmute-guest": "un-mute guest",
"undeafen": "un-deafen",
"deafen": "deafen guest",
"unblind": "un-blind",
"blind": "blind guest",
"unmute": "un-mute",
"mute-guest": "mute guest",
"unhide": "unhide guest",
"hide-guest": "hide guest",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

121
examples/bigmutebutton.html Normal file
View File

@ -0,0 +1,121 @@
<html>
<head><title>Twitch + Video</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<style>
body{
padding:0;
margin:0;
background-color:#003;
width:100%;
height:100%;
}
iframe {
width:100%;
height:100%;
border:0;
margin:0;
padding:0;
position:absolute;
display:block;
}
input{
padding:10px;
width:80%;
font-size:1.2em;
z-index: 1000;
margin:10%;
}
#startButton{
margin: 10px;
padding: 20px
display: block;
border-radius: 50px;
font-size:1.5em;
}
#toggleMute{
margin: 10px;
padding: 30px 0;
border-radius: 50px;
font-size:1.5em;
display: block;
position: fixed;
bottom: 0;
width:200px;
left: calc(50% - 100px);
}
.pressed {
background-color: red;
}
</style>
</head>
<body>
<div id="clean">
<center>
<input placeholder="Enter a VDON stream ID here" id="viewlink" type="text" />
<button id="startButton" onclick="loadIframes()" style="display:block;padding:10px;margin:10px;">START</button></center>
</div>
<script>
window.addEventListener("orientationchange", function() {
// Announce the new orientation number
// alert(window.orientation);
}, false);
function loadIframes(url=false){
var streamID = document.getElementById("viewlink").value;
https://vdo.ninja/?label&webcam&cleanoutput&ad=1&vd=1
var path = "vdo.ninja"; //window.location.host+window.location.pathname.split("/").slice(0,-1).join("/");
var streamSrc = "https://"+path+"/?push="+streamID+"&label&webcam&cleanoutput&ad=1&vd=1";
document.getElementById("clean").parentNode.removeChild(document.getElementById("clean"));
var iframe = document.createElement("iframe");
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
iframe.src = streamSrc;
document.body.appendChild(iframe);
function sendSelfCommand(action, value=null){
iframe.contentWindow.postMessage({"target":null, "action":action, "value":value}, '*');
}
var button = document.createElement("button");
button.id = "toggleMute";
button.innerHTML = "Mute";
button.dataset.value = "true";
document.body.appendChild(button);
button.onclick = function(){
if (this.dataset.value=="true"){
this.dataset.value = "false";
this.classList.add("pressed");
this.innerHTML = "Un-Mute";
sendSelfCommand("mic",false);
} else {
this.classList.remove("pressed");
this.innerHTML = "Mute";
this.dataset.value = "true";
sendSelfCommand("mic",true);
}
}
}
</script>
</body>
</html>

159
examples/chat.html Normal file
View File

@ -0,0 +1,159 @@
<html>
<head>
<meta charset="utf8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OBSN Chat Overlay</title>
<style>
@font-face {
font-family: 'Cousine';
src: url('fonts/Cousine-Bold.ttf') format('truetype');
}
body {
margin:0;
padding:0 10px;
height:100%;
border: 0;
display: flex;
flex-direction: column-reverse;
position:absolute;
bottom:0;
overflow:hidden;
max-width:100%;
}
div {
margin:0;
background-color: black;
padding: 8px 8px 0px 8px;
color: white;
font-family: Cousine, monospace;
font-size: 3.2em;
line-height: 1.1em;
letter-spacing: 0.0em;
text-transform: uppercase;
text-shadow: 0.05em 0.05em 0px rgba(0,0,0,1);
max-width:100%;
word-wrap: break-word;
overflow-wrap: break-word;
word-break: break-all;
hyphens: auto;
display:inline-block;
}
a {
color:white;
font-size:1.2em;
text-transform: none;
word-wrap: break-word;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
hyphens: auto;
}
</style>
<script>
(function (w) {
w.URLSearchParams =
w.URLSearchParams ||
function (searchString) {
var self = this;
self.searchString = searchString;
self.get = function (name) {
var results = new RegExp("[\?&]" + name + "=([^&#]*)").exec(
self.searchString
);
if (results == null) {
return null;
} else {
return decodeURI(results[1]) || 0;
}
};
};
})(window);
var urlParams = new URLSearchParams(window.location.search);
function loadIframe() {
var iframe = document.createElement("iframe");
var view= "";
if (urlParams.has("view")) {
view = "&view="+(urlParams.get("view") || "");
}
var room="";
if (urlParams.has("room")) {
room = "&room="+urlParams.get("room");
}
var password="";
if (urlParams.has("password")) {
password = "&password="+urlParams.get("password");
}
iframe.allow = "autoplay";
var srcString = "./?novideo&noaudio&label=chatOverlay&scene"+room+view+password;
iframe.src = srcString;
iframe.style.width="0";
iframe.style.height="0";
iframe.style.border="0";
document.body.appendChild(iframe);
//////////// LISTEN FOR EVENTS
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
console.log(e);
if ("gotChat" in e.data){
logData(e.data.gotChat.label,e.data.gotChat.msg);
}
});
}
function printValues(obj) {
var out = "";
for (var key in obj) {
if (typeof obj[key] === "object") {
out += "<br />";
out += printValues(obj[key]);
} else {
if (key.startsWith("_")) {
} else {
out += "<b>" + key + "</b>: " + obj[key] + "<br />";
}
}
}
return out;
}
function logData(type, data) {
var span = document.createElement('span');
var entry = document.createElement('div');
if (type){
type = "<i>"+type.replace(/_/g, ' ')+"</i>";
}
entry.innerHTML = type + data;
span.appendChild(entry);
document.body.prepend(span);
}
</script>
</head>
<body onload="loadIframe();">
</body>
</html>

331
examples/draggable.html Normal file
View File

@ -0,0 +1,331 @@
<html>
<head><title>Dual Input</title>
<style>
body{
padding:0;
margin:0;
}
iframe {
border:0;
margin:0;
padding:0;
display:block;
margin:0px;
min-height: 100px;
min-width: 100px;
max-height: 95%;
max-width: 99%%;
float: left;
position: fixed;
}
#viewlink {
width:400px;
}
input{
padding:5px;
margin:5px;
}
button{
padding:5px;
margin:5px;
position:relative;
}
.menu {
z-index: 10;
float:right;
right: 20px;
color: #fff;
}
.close {
background-color: #d33;
color: #fff;
}
.reload {
background-color: #0a0;
color: #fff;
}
.popup {
z-index: 9;
background-color: #f1f1f1;
border: 1px solid #d3d3d3;
text-align: center;
min-height: 100px;
min-width: 100px;
max-height: 95%;
max-width: 99%;
scale: 0.5;
}
.popup {
position: absolute;
/*resize: both; !*enable this to css resize*! */
overflow: auto;
}
.popup-header {
cursor: move;
background-color: #2196f3;
}
.popup .resizer-right {
width: 5px;
height: 100%;
background: transparent;
position: absolute;
right: 0;
bottom: 0;
cursor: e-resize;
}
.popup .resizer-bottom {
width: 100%;
height: 5px;
background: transparent;
position: absolute;
right: 0;
bottom: 0;
cursor: n-resize;
}
.popup .resizer-both {
width: 5px;
height: 5px;
background: transparent;
z-index: 10;
position: absolute;
right: 0;
bottom: 0;
cursor: nw-resize;
}
/*NOSELECT*/
.popup * {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
}
</style>
</head>
<body>
<input placeholder="Enter an OBS.Ninja Room Link" id="viewlink" />
<button onclick="loadIframe();">Load URL</button>You can drag and resize the generated windows; multiple can be created.
<div id="container"></div>
<script>
var currentZIndex = 100;
function initDragElement(popup){
var pos1 = 0,
pos2 = 0,
pos3 = 0,
pos4 = 0;
var elmnt = null;
var header = getHeader(popup);
var iframe = getIFrame(popup);
popup.onmousedown = function() {
this.style.zIndex = "" + ++currentZIndex;
};
if (header) {
header.parentPopup = popup;
header.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
elmnt = this.parentPopup;
elmnt.style.zIndex = "" + ++currentZIndex;
e = e || window.event;
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
if (!elmnt) {
return;
}
e = e || window.event;
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = elmnt.offsetTop - pos2 + "px";
elmnt.style.left = elmnt.offsetLeft - pos1 + "px";
iframe.style.top = elmnt.offsetTop - pos2 + "px";
iframe.style.left = elmnt.offsetLeft - pos1 + "px";
}
function closeDragElement() {
/* stop moving when mouse button is released:*/
document.onmouseup = null;
document.onmousemove = null;
}
function getHeader(element) {
var headerItems = element.getElementsByClassName("popup-header");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
function getIFrame(element) {
var headerItems = element.getElementsByTagName("iframe");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
}
function initResizeElement(p) {
var iframe = getIFrame(p);
var element = null;
var startX, startY, startWidth, startHeight;
var right = document.createElement("div");
right.className = "resizer-right";
p.appendChild(right);
right.addEventListener("mousedown", initDrag, false);
right.parentPopup = p;
var bottom = document.createElement("div");
bottom.className = "resizer-bottom";
p.appendChild(bottom);
bottom.addEventListener("mousedown", initDrag, false);
bottom.parentPopup = p;
var both = document.createElement("div");
both.className = "resizer-both";
p.appendChild(both);
both.addEventListener("mousedown", initDrag, false);
both.parentPopup = p;
function initDrag(e) {
element = this.parentPopup;
startX = e.clientX;
startY = e.clientY;
startWidth = parseInt(
document.defaultView.getComputedStyle(element).width,
10
);
startHeight = parseInt(
document.defaultView.getComputedStyle(element).height,
10
);
document.documentElement.addEventListener("mousemove", doDrag, false);
document.documentElement.addEventListener("mouseup", stopDrag, false);
document.documentElement.addEventListener("click", stopDrag, false)
}
function doDrag(e) {
if (e.buttons==0){
stopDrag(e);
return false;
}
element.style.width = startWidth + e.clientX - startX + "px";
element.style.height = startHeight + e.clientY - startY + "px";
iframe.style.width = startWidth + e.clientX - startX + "px";
iframe.style.height = startHeight + e.clientY - startY + "px";
}
function stopDrag(e) {
document.documentElement.removeEventListener("mousemove", doDrag, false);
document.documentElement.removeEventListener("mouseup", stopDrag, false);
}
function getIFrame(element) {
var headerItems = element.getElementsByTagName("iframe");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
}
function loadIframe(){
var iframeContainer = document.createElement("div");
iframeContainer.className="popup";
iframeContainer.style.zIndex = "" + ++currentZIndex;
iframeContainer.style.width="325px";
iframeContainer.style.height="420px";
var button = document.createElement("button");
button.innerHTML = "Move";
button.className = "popup-header menu";
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Close";
button.className = "menu close";
button.onclick = function(){iframe.contentWindow.postMessage({"close":true}, '*');iframe.parentNode.parentNode.removeChild(iframeContainer);}
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Reload";
button.className = "menu reload";
button.onclick = function(){iframe.contentWindow.postMessage({"reload":true}, '*');}
iframeContainer.appendChild(button);
var iframe = document.createElement("iframe");
iframe.allow="autoplay";
iframe.src = document.getElementById("viewlink").value || "https://obs.ninja";
iframe.style.width="325px";
iframe.style.height="420px";
iframeContainer.appendChild(iframe);
document.getElementById("container").appendChild(iframeContainer);
initDragElement(iframeContainer);
initResizeElement(iframeContainer);
}
</script>
</body>
</html>

View File

@ -1,328 +1,73 @@
<html>
<head><title>Dual Input</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<style>
body{
padding:0;
margin:0;
background-color:#003;
width:100%;
height:100%;
}
iframe {
width:100%;
height:100%;
border:0;
margin:0;
padding:0;
position:absolute;
display:block;
margin:0px;
min-height: 100px;
min-width: 100px;
max-height: 95%;
max-width: 99%%;
float: left;
position: fixed;
}
#viewlink {
width:400px;
}
input{
padding:5px;
margin:5px;
}
button{
padding:5px;
margin:5px;
position:relative;
}
.menu {
z-index: 10;
float:right;
right: 20px;
color: #fff;
}
.close {
background-color: #d33;
color: #fff;
}
.reload {
background-color: #0a0;
color: #fff;
}
.popup {
z-index: 9;
background-color: #f1f1f1;
border: 1px solid #d3d3d3;
text-align: center;
min-height: 100px;
min-width: 100px;
max-height: 95%;
max-width: 99%;
scale: 0.5;
}
.popup {
position: absolute;
/*resize: both; !*enable this to css resize*! */
overflow: auto;
}
.popup-header {
cursor: move;
background-color: #2196f3;
}
.popup .resizer-right {
width: 5px;
height: 100%;
background: transparent;
position: absolute;
right: 0;
bottom: 0;
cursor: e-resize;
}
.popup .resizer-bottom {
width: 100%;
height: 5px;
background: transparent;
position: absolute;
right: 0;
bottom: 0;
cursor: n-resize;
}
.popup .resizer-both {
width: 5px;
height: 5px;
background: transparent;
z-index: 10;
position: absolute;
right: 0;
bottom: 0;
cursor: nw-resize;
}
/*NOSELECT*/
.popup * {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
padding:10px;
width:80%;
font-size:1.2em;
z-index: 1000;
}
</style>
</head>
<body>
<input placeholder="Enter an OBS.Ninja Room Link" id="viewlink" />
<button onclick="loadIframe();">Load URL</button>You can drag and resize the generated windows; multiple can be created.
<div id="container"></div>
<div id="container1" style="width:100%;height:100%;display:none;"></div>
<div id="container2" style="width: calc(25vh*1.777);height: calc(25vh); display:none; float:left; position: fixed; top: 2%; left: 0%;"></div>
<input placeholder="Enter a Room name" id="viewlink" type="text" onchange="loadIframes()"/>
<script>
var currentZIndex = 100;
function initDragElement(popup){
var pos1 = 0,
pos2 = 0,
pos3 = 0,
pos4 = 0;
function loadIframes(url=false){
var elmnt = null;
var header = getHeader(popup);
var iframe = getIFrame(popup);
popup.onmousedown = function() {
this.style.zIndex = "" + ++currentZIndex;
};
if (header) {
header.parentPopup = popup;
header.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
elmnt = this.parentPopup;
elmnt.style.zIndex = "" + ++currentZIndex;
e = e || window.event;
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
if (!elmnt) {
return;
}
e = e || window.event;
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = elmnt.offsetTop - pos2 + "px";
elmnt.style.left = elmnt.offsetLeft - pos1 + "px";
var roomname = document.getElementById("viewlink").value;
iframe.style.top = elmnt.offsetTop - pos2 + "px";
iframe.style.left = elmnt.offsetLeft - pos1 + "px";
}
function closeDragElement() {
/* stop moving when mouse button is released:*/
document.onmouseup = null;
document.onmousemove = null;
}
function getHeader(element) {
var headerItems = element.getElementsByClassName("popup-header");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
function getIFrame(element) {
var headerItems = element.getElementsByTagName("iframe");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
}
function initResizeElement(p) {
var iframe = getIFrame(p);
var element = null;
var startX, startY, startWidth, startHeight;
var right = document.createElement("div");
right.className = "resizer-right";
p.appendChild(right);
right.addEventListener("mousedown", initDrag, false);
right.parentPopup = p;
var bottom = document.createElement("div");
bottom.className = "resizer-bottom";
p.appendChild(bottom);
bottom.addEventListener("mousedown", initDrag, false);
bottom.parentPopup = p;
var both = document.createElement("div");
both.className = "resizer-both";
p.appendChild(both);
both.addEventListener("mousedown", initDrag, false);
both.parentPopup = p;
function initDrag(e) {
element = this.parentPopup;
startX = e.clientX;
startY = e.clientY;
startWidth = parseInt(
document.defaultView.getComputedStyle(element).width,
10
);
startHeight = parseInt(
document.defaultView.getComputedStyle(element).height,
10
);
document.documentElement.addEventListener("mousemove", doDrag, false);
document.documentElement.addEventListener("mouseup", stopDrag, false);
document.documentElement.addEventListener("click", stopDrag, false)
}
function doDrag(e) {
if (e.buttons==0){
stopDrag(e);
return false;
}
element.style.width = startWidth + e.clientX - startX + "px";
element.style.height = startHeight + e.clientY - startY + "px";
document.getElementById("viewlink").parentNode.removeChild(document.getElementById("viewlink"));
document.getElementById("container1").style.display="inline-block";
document.getElementById("container2").style.display="inline-block";
iframe.style.width = startWidth + e.clientX - startX + "px";
iframe.style.height = startHeight + e.clientY - startY + "px";
}
function stopDrag(e) {
document.documentElement.removeEventListener("mousemove", doDrag, false);
document.documentElement.removeEventListener("mouseup", stopDrag, false);
}
function getIFrame(element) {
var headerItems = element.getElementsByTagName("iframe");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
}
function loadIframe(){
var iframeContainer = document.createElement("div");
iframeContainer.className="popup";
iframeContainer.style.zIndex = "" + ++currentZIndex;
iframeContainer.style.width="325px";
iframeContainer.style.height="420px";
var path = window.location.host+window.location.pathname.split("/").slice(0,-1).join("/");
var button = document.createElement("button");
button.innerHTML = "Move";
button.className = "popup-header menu";
iframeContainer.appendChild(button);
var room1 = "https://"+path+"/?room="+roomname+"&push="+roomname+"_front&webcam&autostart&vd=front&ad=1&exclude="+roomname+"_rear";
var room2 = "https://"+path+"/?room="+roomname+"&push="+roomname+"_rear&webcam&autostart&vd=back&ad=0&view&cleanoutput&nosettings&transparent";
var button = document.createElement("button");
button.innerHTML = "Close";
button.className = "menu close";
button.onclick = function(){iframe.contentWindow.postMessage({"close":true}, '*');iframe.parentNode.parentNode.removeChild(iframeContainer);}
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Reload";
button.className = "menu reload";
button.onclick = function(){iframe.contentWindow.postMessage({"reload":true}, '*');}
iframeContainer.appendChild(button);
var iframe = document.createElement("iframe");
iframe.allow="autoplay";
iframe.src = document.getElementById("viewlink").value || "https://obs.ninja";
iframe.style.width="325px";
iframe.style.height="420px";
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
iframe.src = room1;
var iframeContainer = document.createElement("div");
iframeContainer.appendChild(iframe);
document.getElementById("container1").appendChild(iframeContainer);
document.getElementById("container").appendChild(iframeContainer);
initDragElement(iframeContainer);
initResizeElement(iframeContainer);
setTimeout(function(){
var iframe = document.createElement("iframe");
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
iframe.src = room2;
var iframeContainer = document.createElement("div");
iframeContainer.appendChild(iframe);
document.getElementById("container2").appendChild(iframeContainer);
},3000);
}

3086
examples/main.css Normal file

File diff suppressed because it is too large Load Diff

555
examples/midi.html Normal file
View File

@ -0,0 +1,555 @@
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/webmidi"></script>
<link rel="stylesheet" href="./main.css" />
<style>
.container {
max-width: 80%;
width: fit-content;
margin: 0 auto;
}
h1 {
margin-top: 1em;
margin-bottom: 1em;
padding: 10px;
}
.card {
margin: 10px;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%);
background-color: #ddd;
color: black;
margin-bottom: 1.5em;
}
.card>div {
padding: 10px;
}
.card h2 {
font-size: 1.5em;
padding: 10px;
background-color: #457b9d;
color: white;
border-bottom: 2px solid #3b6a87;
}
small {
font-style: italic;
display: block;
margin-left: 1em;
}
span.warning {
color: rgb(212, 191, 0);
}
input {
padding: 10px;
}
video {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
audio {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
div#processing {
display: none;
justify-content: center;
place-items: center;
position: absolute;
inset: 0;
font-size: 1.5em;
font-weight: bold;
background: #141926;
flex-direction: column;
}
button {
margin:5px;
border:solid black 2px;
}
body {
color:white;
}
a {
color: #225273!important;
}
</style>
<title>VDO.Ninja MIDI Controller</title>
</head>
<body>
<div id="header">
<a id="logoname" href="./" style="text-decoration: none; color: white; margin: 2px">
<span data-translate="logo-header">
<font id="qos">V</font>DO.Ninja
</span>
</a>
</div>
<div class="container">
<div id="info">
<h1>VDO.Ninja MIDI test app</h1>
<div class="card">
<h2>About</h2>
<div>
You can check the console debug logs for added details.
<br /><br />You can download a virtual MIDI I/O controller for windwos here:<br />
http://www.tobias-erichsen.de/software/loopmidi.html
<br /><br />This code uses the WebMIDI.js library, referenced here:<br />
https://github.com/djipco/webmidi
<br /><br />
Below you can test the <a href="https://docs.vdo.ninja/general-settings/midi#midi-pass-through-mode">MIDI hotkey commands</a> for VDO.Ninja below:<br />
</div>
</div>
<div class="card">
<h2>Select the MIDI Output device:</h2>
<div>
<label for="outputdevice">MIDI Output device:</label>
<select name="outputdevice" id="outputdevice">
</select>
</div>
</div>
<div class="card">
<h2>&midi=1</h2>
<div id="container1">
</div>
</div>
<div class="card">
<h2>&midi=3</h2>
<div id="container2">
</div>
</div>
<div class="card">
<h2>&midi=4 ; director</h2>
<div id="container3">
</div>
</div>
<div class="card">
<h2>&midi=4 ; guest 1</h2>
<div id="container4">
</div>
</div>
<div class="card">
<h2>&midi=4 ; guest 2</h2>
<div id="container5">
</div>
</div>
<div class="card">
<h2>Sample Remote Director Control links</h2>
<div id="container6">
</div>
</div>
</div>
</div>
<div id='commands'>
</div>
<script>
// Enable WebMidi.js
WebMidi.enable(function (err) {
if (err) {
console.log("WebMidi could not be enabled.", err);
}
// Viewing available inputs and outputs
console.log(WebMidi.inputs);
console.log(WebMidi.outputs);
var output = WebMidi.outputs[0];
var midiout = 0;
var outputdevice = document.getElementById("outputdevice");
for (var i=0;i<WebMidi.outputs.length;i++){
var opt = document.createElement('option');
opt.value = WebMidi.outputs[i].id;
opt.innerHTML = WebMidi.outputs[i].name + " (id:"+(1+i)+")";
if (i==0){
midiout = opt.value;
opt.selected = true;
}
outputdevice.appendChild(opt);
}
var path = window.location.host+window.location.pathname.split("/").slice(0,-1).join("/");
outputdevice.onchange = function(e){
midiout = outputdevice.value;
output = WebMidi.getOutputById(midiout);
console.log("MIDI DEVICE CHANGED: "+midiout);
var container = document.getElementById("container6");
container.innerHTML = "<br />https://"+path+"/?midiremote=4&director=ROOMNAMEHERE";
container.innerHTML += "<br /><br />";
container.innerHTML += "https://"+path+"/?room=ROOMNAMEHERE&midiout="+(outputdevice.selectedIndex+1)+"&vd=0&ad=0&push&autostart&label=MIDI_CONTROLLER";
}
var container = document.getElementById("container6");
container.innerHTML = "<br />https://"+path+"/?midiremote=4&director=ROOMNAMEHERE";
container.innerHTML += "<br /><br />";
container.innerHTML += "https://"+path+"/?room=ROOMNAMEHERE&midiout="+(outputdevice.selectedIndex+1)+"&vd=0&ad=0&push&autostart&label=MIDI_CONTROLLER";
// Reacting when a new device becomes available
WebMidi.addListener("connected", function(e) {
console.log(e);
});
// Reacting when a device becomes unavailable
WebMidi.addListener("disconnected", function(e) {
console.log(e);
});
// Display the current time
console.log(WebMidi.time);
// Retrieve an input by name, id or index
// var input = WebMidi.getInputByName("StreamDeck2Daw");
// input = WebMidi.getInputById("1809568182");
var input = WebMidi.inputs[1];
// Listen for a 'note on' message on all channels
input.addListener('noteon', "all",
function (e) {
console.log("Received 'noteon' message (" + e.note.name + e.note.octave + ").");
console.log(e);
}
);
// Listen to pitch bend message on channel 3
input.addListener('pitchbend', 3,
function (e) {
console.log("Received 'pitchbend' message.", e);
}
);
// Listen to control change message on all channels
input.addListener('controlchange', "all",
function (e) {
console.log("Received 'controlchange' message.", e);
}
);
// Listen to NRPN message on all channels
input.addListener('nrpn', "all",
function (e) {
if(e.controller.type === 'entry') {
console.log("Received 'nrpn' 'entry' message.", e);
}
if(e.controller.type === 'decrement') {
console.log("Received 'nrpn' 'decrement' message.", e);
}
if(e.controller.type === 'increment') {
console.log("Received 'nrpn' 'increment' message.", e);
}
console.log("message value: " + e.controller.value + ".", e);
}
);
var container = document.getElementById("container1");
///
var button = document.createElement("button");
button.innerHTML = "Note G3; Chat";
button.onclick = function(){output.playNote("G3");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note A3; Mute";
button.onclick = function(){output.playNote("A3");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note B3; Mute Video";
button.onclick = function(){output.playNote("B3");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C4; ScreenShare";
button.onclick = function(){output.playNote("C4");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note D4; Hangup";
button.onclick = function(){output.playNote("D4");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note E4; Hands";
button.onclick = function(){output.playNote("E4");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note F4; Record";
button.onclick = function(){output.playNote("F4");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note G4; Turn on Dir's Audio";
button.onclick = function(){output.playNote("G4");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note A4; Stop Dir's Audio";
button.onclick = function(){output.playNote("A4");}; // "speaker" also works in the same way.
container.appendChild(button);
///
var container = document.getElementById("container2");
///
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 0";
button.onclick = function(){output.playNote("C1", 1, {velocity: 0});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 1";
button.onclick = function(){output.playNote("C1", 1, {velocity: 1});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 2";
button.onclick = function(){output.playNote("C1", 1, {velocity: 2});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 3";
button.onclick = function(){output.playNote("C1", 1, {velocity: 3});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 4";
button.onclick = function(){output.playNote("C1", 1, {velocity: 4});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 5";
button.onclick = function(){output.playNote("C1", 1, {velocity: 5});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 6";
button.onclick = function(){output.playNote("C1", 1, {velocity: 6});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 7";
button.onclick = function(){output.playNote("C1", 1, {velocity: 7});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 8";
button.onclick = function(){output.playNote("C1", 1, {velocity: 8});}; // "speaker" also works in the same way.
container.appendChild(button);
///
var container = document.getElementById("container3");
///
var button = document.createElement("button");
button.innerHTML = "channel 110; value 0";
button.onclick = function(){output.sendControlChange(110, 0, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 1";
button.onclick = function(){output.sendControlChange(110, 1, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 2";
button.onclick = function(){output.sendControlChange(110, 2, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 3";
button.onclick = function(){output.sendControlChange(110, 3, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 4";
button.onclick = function(){output.sendControlChange(110, 4, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 5";
button.onclick = function(){output.sendControlChange(110, 5, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 6";
button.onclick = function(){output.sendControlChange(110, 6, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 7";
button.onclick = function(){output.sendControlChange(110, 7, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 8";
button.onclick = function(){output.sendControlChange(110, 8, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
///
var container = document.getElementById("container4");
///
var button = document.createElement("button");
button.innerHTML = "channel 111; value 0";
button.onclick = function(){output.sendControlChange(111, 0, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 111; value 1";
button.onclick = function(){output.sendControlChange(111, 1, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 111; value 2";
button.onclick = function(){output.sendControlChange(111, 2, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 111; value 3";
button.onclick = function(){output.sendControlChange(111, 3, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 111; value 4";
button.onclick = function(){output.sendControlChange(111, 4, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 111; value 5";
button.onclick = function(){output.sendControlChange(111, 5, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
///
var container = document.getElementById("container5");
///
var button = document.createElement("button");
button.innerHTML = "channel 112; transfer popup";
button.onclick = function(){output.sendControlChange(112, 0, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 112; scene 1";
button.onclick = function(){output.sendControlChange(112, 1, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 112; mute in scene";
button.onclick = function(){output.sendControlChange(112, 2, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 112; mute everywhere";
button.onclick = function(){output.sendControlChange(112, 3, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 112; hang up";
button.onclick = function(){output.sendControlChange(112, 4, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 112; solo chat";
button.onclick = function(){output.sendControlChange(112, 5, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "remote speaker";
button.onclick = function(){output.sendControlChange(112, 6, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "remote display";
button.onclick = function(){output.sendControlChange(112, 7, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 2";
button.onclick = function(){output.sendControlChange(112, 12, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 3";
button.onclick = function(){output.sendControlChange(112, 13, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 4";
button.onclick = function(){output.sendControlChange(112, 14, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 5";
button.onclick = function(){output.sendControlChange(112, 15, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 6";
button.onclick = function(){output.sendControlChange(112, 16, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = " scene 7";
button.onclick = function(){output.sendControlChange(112, 17, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 8";
button.onclick = function(){output.sendControlChange(112, 18, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
});
</script>
</body>
</html>

97
examples/minidirector.css Normal file
View File

@ -0,0 +1,97 @@
body{
zoom: 75%;
}
.hidden{
display:unset!important;
visibility: visible;
width:unset;
height:unset;
opacity: 1;
}
button[data-action-type='solo-chat'] {
display:none! important;
}
button[data-action-type='recorder-local'] {
display:none! important;
}
span[data-action-type='ordering'] {
display:none! important;
}
button[data-action-type='open-file-share'] {
display:none! important;
}
button[data-action-type='add-channel']{
display:none! important;
}
button[data-action-type='toggle-remote-speaker']{
display:none! important;
}
button[data-action-type='toggle-remote-display']{
display:none! important;
}
button[data-action-type='hide-guest']{
display:none! important;
}
button[data-action-type='create-timer']{
display:none! important;
}
button[data-action-type='change-url']{
display:none! important;
}
button[data-action-type='change-params']{
display:none! important;
}
span[data-action-type='change-quality']{
display:none! important;
}
span[data-action-type='sceneCluster2']{
display:none! important;
}
span[data-action-type='sceneCluster1']{
display:none! important;
}
.orderspan{
display:none! important;
}
#roomHeader{
display:none! important;
}
.directorContainer {
display:none!important;
}
.hideDropMenu{
display:none!important;
}
#header{
display:none!important;
}
body {
background-color: #000;
}
button[class="pull-right"]{
display:none! important;
}
div#guestFeeds {
padding: 1px!important;
margin: 1px!important;
}
div[class="vidcon directorMargins"] {
padding: 1px!important;
margin: 1px!important;
width: 260px;
}
button[data-action-type]{
margin: 1px!important;
}

486
examples/obs_client.html Normal file
View File

@ -0,0 +1,486 @@
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="./thirdparty/obs-websocket.min.js"></script>
<link rel="stylesheet" href="./main.css" />
<style>
.container {
max-width: 80%;
width: fit-content;
margin: 0 auto;
}
h1 {
margin-top: 1em;
margin-bottom: 1em;
padding: 10px;
}
.card {
margin: 10px;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%);
background-color: #ddd;
color: black;
margin-bottom: 1.5em;
}
.card>div {
padding: 10px;
}
.card h2 {
font-size: 1.5em;
padding: 10px;
background-color: #457b9d;
color: white;
border-bottom: 2px solid #3b6a87;
}
small {
font-style: italic;
display: block;
margin-left: 1em;
}
span.warning {
color: rgb(212, 191, 0);
}
input {
padding: 10px;
display: inline-block;
flex-flow: unset;
margin: 10px;
}
video {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
audio {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
div#processing {
display: none;
justify-content: center;
place-items: center;
position: absolute;
inset: 0;
font-size: 1.5em;
font-weight: bold;
background: #141926;
flex-direction: column;
}
button {
margin:5px;
border:solid black 2px;
}
body {
color:white;
display: inline-block;
flex-flow: unset;
}
a {
color: #225273!important;
}
.hidden{
display:none !important;
}
.stat {
background-color: black;
margin: 7px;
padding: 5px;
}
.stat:nth-child(2n) {
background-color: #333;
}
.stat:empty {
display:none;
}
</style>
<title>OBS Controller Demo using VDO.NInja</title>
</head>
<div class="container">
<h1>OBS remote (client)</h1>
<div id="info">
<div class="card">
<h2>Scenes</h2>
<div id="scene_list">
</div>
</div>
<div class="card hidden">
<h2>General</h2>
<div>
<button onclick="basicCommand(this);" data-command="GetVersion">GetVersion</button>
<button onclick="basicCommand(this);" data-command="GetStats">GetStats</button>
<button onclick="basicCommand(this);" data-command="GetVideoInfo">GetVideoInfo</button>
</div>
</div>
<div class="card">
<h2>Output</h2>
<div id="outputs">
<button onclick="basicCommand(this);" data-command="ListOutputs">ListOutputs</button>
</div>
</div>
<div class="card">
<h2>Active Sources</h2>
<div id="active_source_list">
</div>
</div>
<div class="card">
<h2>All Sources</h2>
<div id="source_list">
</div>
</div>
<div class="card hidden">
<h2>Source debug</h2>
<div>
<button onclick="basicCommand(this);" data-command="GetMediaSourcesList">GetMediaSourcesList</button>
<button onclick="basicCommand(this);" data-command="GetSourcesList">GetSourcesList</button>
<button onclick="basicCommand(this);" data-command="GetSourceActive">GetSourceActive</button>
<button onclick="basicCommand(this);" data-command="GetAudioActive">GetAudioActive</button>
</div>
</div>
<div id="audio" class="card hidden">
<h2>Audio</h2>
<div>
<button class="hidden" onclick="basicCommand(this, {source:selectedSource});" data-command="GetVolume">GetVolume</button>
<input type='range' min="0" max="100" value="100" onchange="basicCommand(this, {source:selectedSource, volume:parseInt(this.value)/100});" data-command="SetVolume" />
<button class="hidden" onclick="basicCommand(this, {source:selectedSource});" data-command="ToggleMute">ToggleMute</button>
</div>
</div>
<div class="card" id='commands'>
<h2>Custom Commands</h2>
<input id="newCommand" placeholder="enter a custom command" type='text' /><button id="goCreate" onclick="createCommand()">Create</button>
<br />
A list of possible commands <a href="https://github.com/Palakis/obs-websocket/blob/4.x-current/docs/generated/protocol.md#requests">available here:</a><br />
</div>
</div>
<div style="width:100%;display:block;margin:0;padding:0;">
<div id='OBSstats' style="width:49%;display:inline-block;vertical-align: top;">
<div id="stat-current-profile" class='stat'></div>
<div id="stat-current-scene" class='stat'></div>
<div id="stat-streaming" class='stat'></div>
<div id="stat-memory-usage" class='stat'></div>
<div id="stat-recording" class='stat'></div>
<div id="stat-recording-paused" class='stat'></div>
<div id="stat-average-frame-time" class='stat'></div>
<div id="stat-cpu-usage" class='stat'></div>
<div id="stat-fps" class='stat'></div>
<div id="stat-free-disk-space" class='stat'></div>
</div>
<div id='OBSsettings' style="width:49%;display:inline-block;vertical-align: top;">
<div id="setting-baseHeight" class='stat'></div>
<div id="setting-baseWidth" class='stat'></div>
<div id="setting-outputHeight" class='stat'></div>
<div id="setting-outputWidth" class='stat'></div>
<div id="setting-scaleType" class='stat'></div>
<div id="setting-fps" class='stat'></div>
</div>
</div>
</div>
<script>
function basicCommand(ele, data={}){
sendToOBS(ele.dataset.command, data);
}
function createCommand(){
var command = document.getElementById("newCommand").value;
document.getElementById("newCommand").value = "";
var button = document.createElement("button");
button.innerText = command;
button.dataset.command = command;
button.onclick = function(){basicCommand(this);};
document.getElementById("commands").appendChild(button);
}
(function(w) {
w.URLSearchParams = w.URLSearchParams || function(searchString) {
var self = this;
searchString = searchString.replace("??", "?");
self.searchString = searchString;
self.get = function(name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString);
if (results == null) {
return null;
} else {
return decodeURI(results[1]) || 0;
}
};
};
})(window);
var urlEdited = window.location.search.replace(/\?\?/g, "?");
urlEdited = urlEdited.replace(/\?/g, "&");
urlEdited = urlEdited.replace(/\&/, "?");
if (urlEdited !== window.location.search){
warnlog(window.location.search + " changed to " + urlEdited);
window.history.pushState({path: urlEdited.toString()}, '', urlEdited.toString());
}
var urlParams = new URLSearchParams(urlEdited);
if (urlParams.get("password")){
var password = urlParams.get("password");
localStorage.setItem('password',password)
} else if (localStorage.getItem('password')){
password = localStorage.getItem('password');
}
if (urlParams.get("room")){
var roomname = urlParams.get("room")
localStorage.setItem('roomname',roomname)
} else if (localStorage.getItem('roomname')){
roomname = localStorage.getItem('roomname');
}
const obs = new OBSWebSocket();
var scenesData = {};
var selectedSource = null;
scenesData.scenes = [];
function sendToOBS(action, data){
var msg = {};
msg.sendToOBS = {};
msg.sendToOBS.action = action;
msg.sendToOBS.data = data;
iframe.contentWindow.postMessage({"sendData":msg}, '*');
}
var iframe = document.createElement("iframe");
iframe.src = "./?room="+roomname+"&password="+password+"&push&novideo=mainOBSOutput&noaudio=mainOBSOutput&autostart&vd=0&ad=0&transparent&cleanoutput&label=CLIENT_"+Math.floor(Math.random() * 1000000);
// iframe.style.opacity = 0;
iframe.style.width = "160px";
iframe.style.height = "90px";
iframe.style.position = "fixed";
iframe.style.top = "10px";
iframe.style.right = "170px";
document.body.appendChild(iframe)
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if ("dataReceived" in e.data){
if ("sentFromOBS" in e.data.dataReceived){
processIncoming(e.data.dataReceived.sentFromOBS);
}
}
});
function changeScene(scene){
sendToOBS('SetCurrentScene', {
'scene-name': scene
});
}
function processIncoming(data){
if ("scenes" in data){
scenesData = data.scenes;
updateSceneList();
}
if ("callbackData" in data){
console.log(data.callbackData);
} else if ("callbackError" in data){
console.log(data.callbackError);
}
if ("rawData" in data){
var data = JSON.parse(data.rawData);
console.log(data);
if (("update-type" in data) && (data["update-type"]="Heartbeat")){
for (var i in data){
if (i === "stats"){
for (var j in data[i]){
if (document.getElementById("stat-"+j)){
if (typeof data[i][j] == "number"){
data[i][j] = parseInt(data[i][j]*100)/100.0;
}
if (data[i][j]===true){
document.getElementById("stat-"+j).innerHTML = j+ " : <font color='#CFC'>" + data[i][j]+"</font>";
} else if (data[i][j] === false){
document.getElementById("stat-"+j).innerHTML = j+ " : <font color='#FCC'>" + data[i][j]+"</font>";
} else {
document.getElementById("stat-"+j).innerHTML = j+ " : <font color='#DDD'>" + data[i][j]+"</font>";
}
}
}
} else if (document.getElementById("stat-"+i)){
if (data[i]===true){
document.getElementById("stat-"+i).innerHTML = i+ " : <font color='#CFC'>" + data[i]+"</font>";
} else if (data[i] === false){
document.getElementById("stat-"+i).innerHTML = i+ " : <font color='#FCC'>" + data[i]+"</font>";
} else {
document.getElementById("stat-"+i).innerHTML = i+ " : <font color='#DDD'>" + data[i]+"</font>";
}
}
}
} else if ("baseHeight" in data){
for (var i in data){
if (document.getElementById("setting-"+i)){
if (data[i]===true){
document.getElementById("setting-"+i).innerHTML = i+ " : <font color='#CFC'>" + data[i]+"</font>";
} else if (data[i] === false){
document.getElementById("setting-"+i).innerHTML = i+ " : <font color='#FCC'>" + data[i]+"</font>";
} else {
document.getElementById("setting-"+i).innerHTML = i+ " : <font color='#DDD'>" + data[i]+"</font>";
}
}
}
} else if ("sources" in data){
if ("name" in data){
document.getElementById("active_source_list").innerHTML = "";
for (var i =0;i<data.sources.length;i++){
var button = document.createElement("button");
button.innerText = data.sources[i].name;
button.dataset.name = data.sources[i].name;
button.dataset.type = "source"
button.onclick = function(){
console.log("CLICKED: " + this.innerText);
selectedSource = this.dataset.name;
document.getElementById("audio").classList.add("hidden");
var sources = document.querySelectorAll("[data-name");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
document.getElementById("audio").classList.remove("hidden");
sources[k].classList.add("pressed");
}
};
document.getElementById("active_source_list").appendChild(button);
}
if (selectedSource){
var sources = document.querySelectorAll("[data-name");
document.getElementById("audio").classList.add("hidden");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.add("pressed");
document.getElementById("audio").classList.remove("hidden");
}
} else {
document.getElementById("audio").classList.add("hidden");
}
} else {
document.getElementById("source_list").innerHTML = "";
for (var i =0;i<data.sources.length;i++){
var button = document.createElement("button");
button.innerText = data.sources[i].name;
button.dataset.name = data.sources[i].name;
button.dataset.type = "source"
button.onclick = function(){
console.log("CLICKED: " + this.innerText);
selectedSource = this.dataset.name;
document.getElementById("audio").classList.add("hidden");
var sources = document.querySelectorAll("[data-name");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.add("pressed");
document.getElementById("audio").classList.remove("hidden");
}
};
document.getElementById("source_list").appendChild(button);
}
if (selectedSource){
var sources = document.querySelectorAll("[data-name");
document.getElementById("audio").classList.add("hidden");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.add("pressed");
document.getElementById("audio").classList.remove("hidden");
}
} else {
document.getElementById("audio").classList.add("hidden");
}
}
<!-- congestion: 0 -->
<!-- droppedFrames: 0 -->
<!-- flags: {audio: true, encoded: true, multiTrack: true, rawValue: 31, service: true, …} -->
<!-- height: 1080 -->
<!-- name: "simple_stream" -->
<!-- reconnecting: false -->
<!-- settings: {bind_ip: 'default', dyn_bitrate: false, low_latency_mode_enabled: false, new_socket_loop_enabled: false} -->
<!-- totalBytes: 351121 -->
<!-- totalFrames: 30 -->
<!-- type: "rtmp_output" -->
<!-- width: 1920 -->
} else if ("outputs" in data){
document.getElementById("outputs").innerHTML = "";
for (var i =0;i<data.outputs.length;i++){
var button = document.createElement("button");
button.innerText = data.outputs[i].name;
button.dataset.output = data.outputs[i].name;
button.onclick = function(){
console.log("CLICKED: " + this.innerText);
var outputName = this.dataset.output;
if (this.classList.contains("pressed")){
this.classList.remove("pressed");
sendToOBS("StopOutput",{outputName:outputName});
} else {
this.classList.add("pressed");
sendToOBS("StartOutput",{outputName:outputName});
}
};
document.getElementById("outputs").appendChild(button);
}
}
}
}
function updateSceneList(){
var scenes = scenesData.scenes;
document.getElementById("scene_list").innerHTML = "";
scenes.forEach(scene => {
var button = document.createElement("button");
button.innerText = scene.name;
button.onclick = function(){
console.log("CLICKED");
changeScene(this.innerText);
}; // "speaker" also works in the same way.
document.getElementById("scene_list").appendChild(button);
if (scene.name === scenesData.currentScene) {
button.classList.add("pressed");
}
});
}
</script>
</body>
</html>

View File

@ -0,0 +1,401 @@
<html>
<head>
<script type="text/javascript" src="./thirdparty/obs-websocket.min.js"></script>
<link rel="stylesheet" href="https://vdo.ninja/main.css" />
<title>OBS Controller Demo using VDO.Ninja</title>
<style>
.container {
max-width: 80%;
width: fit-content;
margin: 0 auto;
}
h1 {
margin-top: 1em;
margin-bottom: 1em;
padding: 10px;
}
.card {
margin: 10px;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%);
background-color: #ddd;
color: black;
margin-bottom: 1.5em;
}
.card>div {
padding: 10px;
}
.card h2 {
font-size: 1.5em;
padding: 10px;
background-color: #457b9d;
color: white;
border-bottom: 2px solid #3b6a87;
}
small {
font-style: italic;
display: block;
margin-left: 1em;
}
span.warning {
color: rgb(212, 191, 0);
}
input {
padding: 10px;
display: inline-block;
flex-flow: unset;
margin: 10px;
}
video {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
audio {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
div#processing {
display: none;
justify-content: center;
place-items: center;
position: absolute;
inset: 0;
font-size: 1.5em;
font-weight: bold;
background: #141926;
flex-direction: column;
}
button {
margin:5px;
border:solid black 2px;
}
body {
color:white;
display: inline-block;
flex-flow: unset;
}
a {
color: #CEF!important;
}
#info{
margin: 20px;
max-height: 50%;
overflow: auto;
}
#client {
margin:10px;
display:block;
}
label {
color:white;
}
</style>
</head>
<body>
<div class="container">
<h1>OBS remote (server)</h1>
<span id='setup'>
<label for="address">Websocket Address</label>
<input name="address" id="address" placeholder="address (optional)" value="localhost:4444" />
<label for="address">Websocket Password</label>
<input name="password" id="password" placeholder="password here (optional)" />
<br />
<label for="vdoroomname">Room name to use</label>
<input name="vdoroomname" id="vdoroomname" placeholder="vdo room name to use (optional)" />
<label for="vdopassword">Room password to use</label>
<input name="vdopassword" id="vdopassword" placeholder="vdo password to use (optional)" />
<br />
<button id="address_button">Connect</button>
<button id="address_button_2">Connect and share OBS Output</button>
</span>
<a id="client" target="_blank"></a>
<div id="info"></div>
<small>Code available on GitHub: <a target="_blank" href='https://github.com/steveseguin/remote_ninja'>https://github.com/steveseguin/remote_ninja</a></small>
</div>
<script>
var hostname = "vdo.ninja"; // all that's supported as of this moment.
const obs = new OBSWebSocket();
var scenesData = {};
scenesData.scenes = [];
function sendToOBS(action, data={}){
document.getElementById("info").innerHTML = action + "<br />"+document.getElementById("info").innerHTML;
obs.sendCallback(action, data, sendCallback)
}
(function(w) {
w.URLSearchParams = w.URLSearchParams || function(searchString) {
var self = this;
searchString = searchString.replace("??", "?");
self.searchString = searchString;
self.get = function(name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString);
if (results == null) {
return null;
} else {
return decodeURI(results[1]) || 0;
}
};
};
})(window);
var urlEdited = window.location.search.replace(/\?\?/g, "?");
urlEdited = urlEdited.replace(/\?/g, "&");
urlEdited = urlEdited.replace(/\&/, "?");
if (urlEdited !== window.location.search){
warnlog(window.location.search + " changed to " + urlEdited);
window.history.pushState({path: urlEdited.toString()}, '', urlEdited.toString());
}
var urlParams = new URLSearchParams(urlEdited);
var roomname = Math.floor(Math.random() * 1000000);
var pwurl = Math.floor(Math.random() * 1000000);
if (urlParams.get("password")){
pwurl = urlParams.get("password");
localStorage.setItem('password',pwurl)
} else if (localStorage.getItem('password')){
pwurl = localStorage.getItem('password');
} else {
localStorage.setItem('password',pwurl)
}
if (urlParams.get("room")){
roomname = urlParams.get("room")
localStorage.setItem('roomname',roomname)
} else if (localStorage.getItem('roomname')){
roomname = localStorage.getItem('roomname');
} else {
localStorage.setItem('roomname',roomname)
}
document.getElementById('vdoroomname').value = roomname;
document.getElementById('vdopassword').value = pwurl;
if (localStorage.getItem('address')){
document.getElementById('address').value = localStorage.getItem('address');
}
if (localStorage.getItem('wspass')){
document.getElementById('password').value = localStorage.getItem('wspass');
}
var iframe = null;
function createIFrame(visible=true){
iframe = document.createElement("iframe");
if (visible){
iframe.src = "https://"+hostname+"/?room="+roomname+"&push=mainOBSOutput&od=0&transparent&webcam&vd=obs&view&password="+pwurl+"&label=OBS_"+Math.floor(Math.random() * 1000000);
iframe.style.minWidth = "720px";
iframe.style.minHeight = "480px";
iframe.style.maxWidth = "50%";
iframe.style.maxHeight = "50%";
iframe.style.display = "block";
iframe.style.margin = "auto";
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
} else {
iframe.src = "https://"+hostname+"/?room="+roomname+"&push&autostart&vd=0&view&ad=0&transparent&cleanoutput&password="+pwurl+"&label=OBS_"+Math.floor(Math.random() * 1000000);
iframe.style.opacity = 0;
iframe.style.width = 0;
iframe.style.height = 0;
iframe.style.position = "absolulte";
iframe.style.top = "-100px";
iframe.style.left = "-100px";
}
document.getElementById("client").parentNode.insertBefore(iframe, document.getElementById("client").nextSibling);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
console.log(e);
if ("dataReceived" in e.data){
if ("sendToOBS" in e.data.dataReceived){
if ("action" in e.data.dataReceived.sendToOBS){
if ("data" in e.data.dataReceived.sendToOBS){
sendToOBS(e.data.dataReceived.sendToOBS.action, e.data.dataReceived.sendToOBS.data);
}
}
}
} else if ("action" in e.data){
if (e.data.action === "new-push-connection"){
console.log(e.data);
updateSceneList();
}
}
});
}
function sendCallback(err, data){
console.log("CALLBACK TRIGGERED");
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.callbackData = data;
msg.sentFromOBS.callbackError = err;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
}
function sendRawData(data){
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.rawData = data;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
}
function heartBeat(){
obs.send("GetStats");
}
function updateSceneList(){
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.scenes = scenesData;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
console.log(msg);
obs.send("GetSourcesList");
obs.send('GetCurrentScene');
obs.send("GetVideoInfo");
obs.send("ListOutputs");
}
document.getElementById('address_button').addEventListener('click', e => {
connect(e, false);
});
document.getElementById('address_button_2').addEventListener('click', e => {
connect(e, true);
});
var heartBeatInterval = null;
function connect(e, camera){
const address = document.getElementById('address').value || "localhost:4444";
const password = document.getElementById('password').value;
roomname = document.getElementById('vdoroomname').value || Math.floor(Math.random() * 1000000);
pwurl = document.getElementById('vdopassword').value || Math.floor(Math.random() * 1000000);
localStorage.setItem('roomname',roomname)
localStorage.setItem('password',pwurl)
createIFrame(camera); // connects to VDO.Ninja's IFRAME API
localStorage.setItem('address',address);
if (password){
localStorage.setItem('wspass',password);
var ret = obs.connect({
address: address,
password: password
});
} else {
var ret = obs.connect({
address: address
});
}
ret.then(() => {
console.log(`Success!`);
return obs.send('GetSceneList');
}).then(data => {
document.getElementById("setup").style.display = "none";
scenesData = data;
updateSceneList();
var pathname = window.location.pathname.split("/");
pathname.pop();
pathname = pathname.join("/");
var clientLink = window.location.protocol + "//" + window.location.host + pathname + "/interface.html?room="+roomname+"&password="+pwurl;
document.getElementById("client").href = clientLink;
document.getElementById("client").innerHTML = "<b><font style='color:#70c4ff;'>client link:</font></b> "+clientLink;
document.getElementById("info").innerHTML = "<br /><p style='color:#bdffbd;'>Connection to OBS websockets opened.</p>" + document.getElementById("info").innerHTML;
try {
obs._socket.onmessage2 = obs._socket.onmessage; // hijacking the obs-websocket.js framework
obs._socket.onmessage = function(data){
console.log(data);
obs._socket.onmessage2(data);
if (data.type && data.data){
if (data.type == "message"){
sendRawData(data.data);
}
}
}
} catch(e){console.error(e);}
clearInterval(heartBeatInterval);
heartBeatInterval = setInterval(function(){heartBeat();},3000);
}).catch(err => { // Promise convention dicates you have a catch on every chain.
console.log(err);
document.getElementById("info").innerHTML = "<br />Error trying to connect. Is SSL enabled on your domain?" + document.getElementById("info").innerHTML;
if ("error" in err){
document.getElementById("info").innerHTML = "<br />"+err.error + document.getElementById("info").innerHTML;
}
});
};
// We use the source visibility one and filter visibility web socket commands quite often in shows.
obs.on('SwitchScenes', data => {
console.log(`New Active Scene: ${data.sceneName}`);
scenesData.currentScene = data.sceneName
updateSceneList();
});
obs.on('ConnectionOpened', (data) => function(){
document.getElementById("setup").style.display = "none";
document.getElementById("info").innerHTML = "<br /><p style='color:#bdffbd;'>Connection to OBS websockets opened.</p>" + document.getElementById("info").innerHTML;
});
obs.on('ConnectionClosed', (data) => function(){
document.getElementById("setup").style.display = "unset";
document.getElementById("info").innerHTML = "<br />Connection to OBS websockets closed" + document.getElementById("info").innerHTML;
});
obs.on('AuthenticationSuccess', (data) => function(){
document.getElementById("setup").style.display = "none";
document.getElementById("info").innerHTML = "<br />OBS websockets authenticated" + document.getElementById("info").innerHTML;
});
obs.on('AuthenticationFailure', (data) => function(){
document.getElementById("setup").style.display = "unset";
document.getElementById("info").innerHTML = "<br />Authentication to OBS websockets failed" + document.getElementById("info").innerHTML;
});
obs.on('ScenesChanged', (data) => function(){
scenesData = data;
updateSceneList()
});
// You must add this handler to avoid uncaught exceptions.
obs.on('error', err => {
document.getElementById("info").innerHTML = "<br />OBS websockets error" + document.getElementById("info").innerHTML;
console.error('socket error:', err);
if ("error" in err){
document.getElementById("info").innerHTML = "<br />"+err.error + document.getElementById("info").innerHTML;
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,474 @@
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="./thirdparty/obs-websocket.min.js"></script>
<link rel="stylesheet" href="https://vdo.ninja/main.css" />
<style>
.container {
max-width: 80%;
width: fit-content;
margin: 0 auto;
}
h1 {
margin-top: 1em;
margin-bottom: 1em;
padding: 10px;
}
.card {
margin: 10px;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%);
background-color: #ddd;
color: black;
margin-bottom: 1.5em;
}
.card>div {
padding: 10px;
}
.card h2 {
font-size: 1.5em;
padding: 10px;
background-color: #457b9d;
color: white;
border-bottom: 2px solid #3b6a87;
}
small {
font-style: italic;
display: block;
margin-left: 1em;
}
span.warning {
color: rgb(212, 191, 0);
}
input {
padding: 10px;
display: inline-block;
flex-flow: unset;
margin: 10px;
}
video {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
audio {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
div#processing {
display: none;
justify-content: center;
place-items: center;
position: absolute;
inset: 0;
font-size: 1.5em;
font-weight: bold;
background: #141926;
flex-direction: column;
}
button {
margin:5px;
border:solid black 2px;
}
body {
color:white;
display: inline-block;
flex-flow: unset;
}
a {
color: #225273!important;
}
.hidden{
display:none !important;
}
.stat {
background-color: black;
margin: 7px;
padding: 5px;
}
.stat:nth-child(2n) {
background-color: #333;
}
.stat:empty {
display:none;
}
</style>
<title>OBS Controller Demo using VDO.NInja</title>
</head>
<div class="container">
<h1>OBS remote (client)</h1>
<div id="info">
<div class="card">
<h2>Scenes</h2>
<div id="scene_list">
</div>
</div>
<div class="card hidden">
<h2>General</h2>
<div>
<button onclick="basicCommand(this);" data-command="GetVersion">GetVersion</button>
<button onclick="basicCommand(this);" data-command="GetStats">GetStats</button>
<button onclick="basicCommand(this);" data-command="GetVideoInfo">GetVideoInfo</button>
</div>
</div>
<div class="card">
<h2>Output</h2>
<div id="outputs">
<button onclick="basicCommand(this);" data-command="ListOutputs">ListOutputs</button>
</div>
</div>
<div class="card">
<h2>Active Sources</h2>
<div id="active_source_list">
</div>
</div>
<div class="card">
<h2>All Sources</h2>
<div id="source_list">
</div>
</div>
<div class="card hidden">
<h2>Source debug</h2>
<div>
<button onclick="basicCommand(this);" data-command="GetMediaSourcesList">GetMediaSourcesList</button>
<button onclick="basicCommand(this);" data-command="GetSourcesList">GetSourcesList</button>
<button onclick="basicCommand(this);" data-command="GetSourceActive">GetSourceActive</button>
<button onclick="basicCommand(this);" data-command="GetAudioActive">GetAudioActive</button>
</div>
</div>
<div id="audio" class="card hidden">
<h2>Audio</h2>
<div>
<button class="hidden" onclick="basicCommand(this, {source:selectedSource});" data-command="GetVolume">GetVolume</button>
<input type='range' min="0" max="100" value="100" onchange="basicCommand(this, {source:selectedSource, volume:parseInt(this.value)/100});" data-command="SetVolume" />
<button class="hidden" onclick="basicCommand(this, {source:selectedSource});" data-command="ToggleMute">ToggleMute</button>
</div>
</div>
<div class="card" id='commands'>
<h2>Custom Commands</h2>
<input id="newCommand" placeholder="enter a custom command" type='text' /><button id="goCreate" onclick="createCommand()">Create</button>
<br />
A list of possible commands <a href="https://github.com/Palakis/obs-websocket/blob/4.x-current/docs/generated/protocol.md#requests">available here:</a><br />
</div>
</div>
<div style="width:100%;display:block;margin:0;padding:0;">
<div id='OBSstats' style="width:49%;display:inline-block;vertical-align: top;">
<div id="stat-current-profile" class='stat'></div>
<div id="stat-current-scene" class='stat'></div>
<div id="stat-streaming" class='stat'></div>
<div id="stat-memory-usage" class='stat'></div>
<div id="stat-recording" class='stat'></div>
<div id="stat-recording-paused" class='stat'></div>
<div id="stat-average-frame-time" class='stat'></div>
<div id="stat-cpu-usage" class='stat'></div>
<div id="stat-fps" class='stat'></div>
<div id="stat-free-disk-space" class='stat'></div>
</div>
<div id='OBSsettings' style="width:49%;display:inline-block;vertical-align: top;">
<div id="setting-baseHeight" class='stat'></div>
<div id="setting-baseWidth" class='stat'></div>
<div id="setting-outputHeight" class='stat'></div>
<div id="setting-outputWidth" class='stat'></div>
<div id="setting-scaleType" class='stat'></div>
<div id="setting-fps" class='stat'></div>
</div>
</div>
</div>
<script>
function basicCommand(ele, data={}){
sendToOBS(ele.dataset.command, data);
}
function createCommand(){
var command = document.getElementById("newCommand").value;
document.getElementById("newCommand").value = "";
var button = document.createElement("button");
button.innerText = command;
button.dataset.command = command;
button.onclick = function(){basicCommand(this);};
document.getElementById("commands").appendChild(button);
}
(function(w) {
w.URLSearchParams = w.URLSearchParams || function(searchString) {
var self = this;
searchString = searchString.replace("??", "?");
self.searchString = searchString;
self.get = function(name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString);
if (results == null) {
return null;
} else {
return decodeURI(results[1]) || 0;
}
};
};
})(window);
var urlEdited = window.location.search.replace(/\?\?/g, "?");
urlEdited = urlEdited.replace(/\?/g, "&");
urlEdited = urlEdited.replace(/\&/, "?");
if (urlEdited !== window.location.search){
warnlog(window.location.search + " changed to " + urlEdited);
window.history.pushState({path: urlEdited.toString()}, '', urlEdited.toString());
}
var urlParams = new URLSearchParams(urlEdited);
if (urlParams.get("password")){
var password = urlParams.get("password");
localStorage.setItem('password',password)
} else if (localStorage.getItem('password')){
password = localStorage.getItem('password');
}
if (urlParams.get("room")){
var roomname = urlParams.get("room")
localStorage.setItem('roomname',roomname)
} else if (localStorage.getItem('roomname')){
roomname = localStorage.getItem('roomname');
}
const obs = new OBSWebSocket();
var scenesData = {};
var selectedSource = null;
scenesData.scenes = [];
function sendToOBS(action, data){
var msg = {};
msg.sendToOBS = {};
msg.sendToOBS.action = action;
msg.sendToOBS.data = data;
iframe.contentWindow.postMessage({"sendData":msg}, '*');
}
var iframe = document.createElement("iframe");
iframe.src = "https://vdo.ninja/?room="+roomname+"&password="+password+"&push&novideo=mainOBSOutput&noaudio=mainOBSOutput&autostart&vd=0&ad=0&transparent&cleanoutput&label=CLIENT_"+Math.floor(Math.random() * 1000000);
// iframe.style.opacity = 0;
iframe.style.width = "160px";
iframe.style.height = "90px";
iframe.style.position = "fixed";
iframe.style.top = "10px";
iframe.style.right = "170px";
document.body.appendChild(iframe)
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if ("dataReceived" in e.data){
if ("sentFromOBS" in e.data.dataReceived){
processIncoming(e.data.dataReceived.sentFromOBS);
}
}
});
function changeScene(scene){
sendToOBS('SetCurrentScene', {
'scene-name': scene
});
}
function processIncoming(data){
if ("scenes" in data){
scenesData = data.scenes;
updateSceneList();
}
if ("callbackData" in data){
console.log(data.callbackData);
} else if ("callbackError" in data){
console.log(data.callbackError);
}
if ("rawData" in data){
var data = JSON.parse(data.rawData);
console.log(data);
if ("stats" in data){
var i = "stats";
for (var j in data[i]){
if (document.getElementById("stat-"+j)){
if (typeof data[i][j] == "number"){
data[i][j] = parseInt(data[i][j]*100)/100.0;
}
if (data[i][j]===true){
document.getElementById("stat-"+j).innerHTML = j+ " : <font color='#CFC'>" + data[i][j]+"</font>";
} else if (data[i][j] === false){
document.getElementById("stat-"+j).innerHTML = j+ " : <font color='#FCC'>" + data[i][j]+"</font>";
} else {
document.getElementById("stat-"+j).innerHTML = j+ " : <font color='#DDD'>" + data[i][j]+"</font>";
}
}
}
} else if ("baseHeight" in data){
for (var i in data){
if (document.getElementById("setting-"+i)){
if (data[i]===true){
document.getElementById("setting-"+i).innerHTML = i+ " : <font color='#CFC'>" + data[i]+"</font>";
} else if (data[i] === false){
document.getElementById("setting-"+i).innerHTML = i+ " : <font color='#FCC'>" + data[i]+"</font>";
} else {
document.getElementById("setting-"+i).innerHTML = i+ " : <font color='#DDD'>" + data[i]+"</font>";
}
}
}
} else if ("sources" in data){
if ("name" in data){
document.getElementById("active_source_list").innerHTML = "";
for (var i =0;i<data.sources.length;i++){
var button = document.createElement("button");
button.innerText = data.sources[i].name;
button.dataset.name = data.sources[i].name;
button.dataset.type = "source"
button.onclick = function(){
console.log("CLICKED: " + this.innerText);
selectedSource = this.dataset.name;
document.getElementById("audio").classList.add("hidden");
var sources = document.querySelectorAll("[data-name");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
document.getElementById("audio").classList.remove("hidden");
sources[k].classList.add("pressed");
}
};
document.getElementById("active_source_list").appendChild(button);
}
if (selectedSource){
var sources = document.querySelectorAll("[data-name");
document.getElementById("audio").classList.add("hidden");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.add("pressed");
document.getElementById("audio").classList.remove("hidden");
}
} else {
document.getElementById("audio").classList.add("hidden");
}
} else {
document.getElementById("source_list").innerHTML = "";
for (var i =0;i<data.sources.length;i++){
var button = document.createElement("button");
button.innerText = data.sources[i].name;
button.dataset.name = data.sources[i].name;
button.dataset.type = "source"
button.onclick = function(){
console.log("CLICKED: " + this.innerText);
selectedSource = this.dataset.name;
document.getElementById("audio").classList.add("hidden");
var sources = document.querySelectorAll("[data-name");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.add("pressed");
document.getElementById("audio").classList.remove("hidden");
}
};
document.getElementById("source_list").appendChild(button);
}
if (selectedSource){
var sources = document.querySelectorAll("[data-name");
document.getElementById("audio").classList.add("hidden");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.add("pressed");
document.getElementById("audio").classList.remove("hidden");
}
} else {
document.getElementById("audio").classList.add("hidden");
}
}
<!-- congestion: 0 -->
<!-- droppedFrames: 0 -->
<!-- flags: {audio: true, encoded: true, multiTrack: true, rawValue: 31, service: true, …} -->
<!-- height: 1080 -->
<!-- name: "simple_stream" -->
<!-- reconnecting: false -->
<!-- settings: {bind_ip: 'default', dyn_bitrate: false, low_latency_mode_enabled: false, new_socket_loop_enabled: false} -->
<!-- totalBytes: 351121 -->
<!-- totalFrames: 30 -->
<!-- type: "rtmp_output" -->
<!-- width: 1920 -->
} else if ("outputs" in data){
document.getElementById("outputs").innerHTML = "";
for (var i =0;i<data.outputs.length;i++){
var button = document.createElement("button");
button.innerText = data.outputs[i].name;
button.dataset.output = data.outputs[i].name;
button.onclick = function(){
console.log("CLICKED: " + this.innerText);
var outputName = this.dataset.output;
if (this.classList.contains("pressed")){
this.classList.remove("pressed");
sendToOBS("StopOutput",{outputName:outputName});
} else {
this.classList.add("pressed");
sendToOBS("StartOutput",{outputName:outputName});
}
};
document.getElementById("outputs").appendChild(button);
}
}
}
}
function updateSceneList(){
var scenes = scenesData.scenes;
document.getElementById("scene_list").innerHTML = "";
scenes.forEach(scene => {
var button = document.createElement("button");
button.innerText = scene.name;
button.onclick = function(){
console.log("CLICKED");
changeScene(this.innerText);
}; // "speaker" also works in the same way.
document.getElementById("scene_list").appendChild(button);
if (scene.name === scenesData.currentScene) {
button.classList.add("pressed");
}
});
}
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

388
examples/obs_ws_dock.html Normal file
View File

@ -0,0 +1,388 @@
<html>
<head>
<script type="text/javascript" src="https://vdo.ninja/beta/thirdparty/obs-websocket.min.js"></script>
<link rel="stylesheet" href="https://vdo.ninja/beta/main.css" />
<title>OBS Controller Demo using VDO.Ninja</title>
<style>
.container {
max-width: 80%;
width: fit-content;
margin: 0 auto;
}
h1 {
margin-top: 1em;
margin-bottom: 1em;
padding: 10px;
}
.card {
margin: 10px;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%);
background-color: #ddd;
color: black;
margin-bottom: 1.5em;
}
.card>div {
padding: 10px;
}
.card h2 {
font-size: 1.5em;
padding: 10px;
background-color: #457b9d;
color: white;
border-bottom: 2px solid #3b6a87;
}
small {
font-style: italic;
display: block;
margin-left: 1em;
}
span.warning {
color: rgb(212, 191, 0);
}
input {
padding: 10px;
display: inline-block;
flex-flow: unset;
margin: 10px;
}
video {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
audio {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
div#processing {
display: none;
justify-content: center;
place-items: center;
position: absolute;
inset: 0;
font-size: 1.5em;
font-weight: bold;
background: #141926;
flex-direction: column;
}
button {
margin:5px;
border:solid black 2px;
}
body {
color:white;
display: inline-block;
flex-flow: unset;
}
a {
color: #CEF!important;
}
#info{
margin: 20px;
max-height: 50%;
overflow: auto;
}
#client {
margin:10px;
display:block;
}
label {
color:white;
}
</style>
</head>
<body>
<div class="container">
<h1>OBS remote (server)</h1>
<span id='setup'>
<label for="address">Websocket Address</label>
<input name="address" id="address" placeholder="address (optional)" value="localhost:4444" />
<label for="address">Websocket Password</label>
<input name="password" id="password" placeholder="password here (optional)" />
<br />
<label for="vdoroomname">Room name to use</label>
<input name="vdoroomname" id="vdoroomname" placeholder="vdo room name to use (optional)" />
<label for="vdopassword">Room password to use</label>
<input name="vdopassword" id="vdopassword" placeholder="vdo password to use (optional)" />
<br />
<button id="address_button">Connect</button>
<button id="address_button_2">Connect and share OBS Output</button>
</span>
<a id="client" target="_blank"></a>
<div id="info"></div>
</div>
<script>
var hostname = "vdo.ninja/beta"; // all that's supported as of this moment.
console.error("TODO: Change hostname");
const obs = new OBSWebSocket();
var scenesData = {};
scenesData.scenes = [];
function sendToOBS(action, data={}){
document.getElementById("info").innerHTML = action + "<br />"+document.getElementById("info").innerHTML;
obs.sendCallback(action, data, sendCallback)
}
(function(w) {
w.URLSearchParams = w.URLSearchParams || function(searchString) {
var self = this;
searchString = searchString.replace("??", "?");
self.searchString = searchString;
self.get = function(name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString);
if (results == null) {
return null;
} else {
return decodeURI(results[1]) || 0;
}
};
};
})(window);
var urlEdited = window.location.search.replace(/\?\?/g, "?");
urlEdited = urlEdited.replace(/\?/g, "&");
urlEdited = urlEdited.replace(/\&/, "?");
if (urlEdited !== window.location.search){
warnlog(window.location.search + " changed to " + urlEdited);
window.history.pushState({path: urlEdited.toString()}, '', urlEdited.toString());
}
var urlParams = new URLSearchParams(urlEdited);
var roomname = Math.floor(Math.random() * 1000000);
var pwurl = Math.floor(Math.random() * 1000000);
if (urlParams.get("password")){
pwurl = urlParams.get("password");
localStorage.setItem('password',pwurl)
} else if (localStorage.getItem('password')){
pwurl = localStorage.getItem('password');
} else {
localStorage.setItem('password',pwurl)
}
if (urlParams.get("room")){
roomname = urlParams.get("room")
localStorage.setItem('roomname',roomname)
} else if (localStorage.getItem('roomname')){
roomname = localStorage.getItem('roomname');
} else {
localStorage.setItem('roomname',roomname)
}
document.getElementById('vdoroomname').value = roomname;
document.getElementById('vdopassword').value = pwurl;
if (localStorage.getItem('address')){
document.getElementById('address').value = localStorage.getItem('address');
}
if (localStorage.getItem('wspass')){
document.getElementById('password').value = localStorage.getItem('wspass');
}
var iframe = null;
function createIFrame(visible=true){
iframe = document.createElement("iframe");
if (visible){
iframe.src = "https://"+hostname+"/?room="+roomname+"&push=mainOBSOutput&od=0&transparent&webcam&vd=obs&view&password="+pwurl+"&label=OBS_"+Math.floor(Math.random() * 1000000);
iframe.style.minWidth = "720px";
iframe.style.minHeight = "480px";
iframe.style.maxWidth = "50%";
iframe.style.maxHeight = "50%";
iframe.style.display = "block";
iframe.style.margin = "auto";
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
} else {
iframe.src = "https://"+hostname+"/?room="+roomname+"&push&autostart&vd=0&view&ad=0&transparent&cleanoutput&password="+pwurl+"&label=OBS_"+Math.floor(Math.random() * 1000000);
iframe.style.opacity = 0;
iframe.style.width = 0;
iframe.style.height = 0;
iframe.style.position = "absolulte";
iframe.style.top = "-100px";
iframe.style.left = "-100px";
}
document.getElementById("client").parentNode.insertBefore(iframe, document.getElementById("client").nextSibling);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
console.log(e);
if ("dataReceived" in e.data){
if ("sendToOBS" in e.data.dataReceived){
if ("action" in e.data.dataReceived.sendToOBS){
if ("data" in e.data.dataReceived.sendToOBS){
sendToOBS(e.data.dataReceived.sendToOBS.action, e.data.dataReceived.sendToOBS.data);
}
}
}
} else if ("action" in e.data){
if (e.data.action === "new-push-connection"){
console.log(e.data);
updateSceneList();
}
}
});
}
function sendCallback(err, data){
console.log("CALLBACK TRIGGERED");
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.callbackData = data;
msg.sentFromOBS.callbackError = err;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
}
function sendRawData(data){
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.rawData = data;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
}
function updateSceneList(){
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.scenes = scenesData;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
console.log(msg);
obs.send("GetSourcesList");
obs.send('GetCurrentScene');
obs.send("GetVideoInfo");
obs.send("ListOutputs");
}
document.getElementById('address_button').addEventListener('click', e => {
connect(e, false);
});
document.getElementById('address_button_2').addEventListener('click', e => {
connect(e, true);
});
function connect(e, camera){
const address = document.getElementById('address').value || "localhost:4444";
const password = document.getElementById('password').value;
roomname = document.getElementById('vdoroomname').value || Math.floor(Math.random() * 1000000);
pwurl = document.getElementById('vdopassword').value || Math.floor(Math.random() * 1000000);
localStorage.setItem('roomname',roomname)
localStorage.setItem('password',pwurl)
createIFrame(camera); // connects to VDO.Ninja's IFRAME API
localStorage.setItem('address',address);
if (password){
localStorage.setItem('wspass',password);
var ret = obs.connect({
address: address,
password: password
});
} else {
var ret = obs.connect({
address: address
});
}
ret.then(() => {
console.log(`Success!`);
return obs.send('GetSceneList');
}).then(data => {
document.getElementById("setup").style.display = "none";
scenesData = data;
updateSceneList();
var clientLink = "https://"+hostname+"/obs_client.html?room="+roomname+"&password="+pwurl;
document.getElementById("client").href = clientLink;
document.getElementById("client").innerHTML = "<b><font style='color:#70c4ff;'>client link:</font></b> "+clientLink;
document.getElementById("info").innerHTML = "<br /><p style='color:#bdffbd;'>Connection to OBS websockets opened.</p>" + document.getElementById("info").innerHTML;
try {
obs._socket.onmessage2 = obs._socket.onmessage;
obs._socket.onmessage = function(data){
obs._socket.onmessage2(data);
if (data.type && data.data){
if (data.type == "message"){
sendRawData(data.data);
}
}
}
} catch(e){console.error(e);}
}).catch(err => { // Promise convention dicates you have a catch on every chain.
console.log(err);
document.getElementById("info").innerHTML = "<br />Error trying to connect." + document.getElementById("info").innerHTML;
if ("error" in err){
document.getElementById("info").innerHTML = "<br />"+err.error + document.getElementById("info").innerHTML;
}
});
};
// We use the source visibility one and filter visibility web socket commands quite often in shows.
obs.on('SwitchScenes', data => {
console.log(`New Active Scene: ${data.sceneName}`);
scenesData.currentScene = data.sceneName
updateSceneList();
});
obs.on('ConnectionOpened', (data) => function(){
document.getElementById("setup").style.display = "none";
document.getElementById("info").innerHTML = "<br /><p style='color:#bdffbd;'>Connection to OBS websockets opened.</p>" + document.getElementById("info").innerHTML;
});
obs.on('ConnectionClosed', (data) => function(){
document.getElementById("setup").style.display = "unset";
document.getElementById("info").innerHTML = "<br />Connection to OBS websockets closed" + document.getElementById("info").innerHTML;
});
obs.on('AuthenticationSuccess', (data) => function(){
document.getElementById("setup").style.display = "none";
document.getElementById("info").innerHTML = "<br />OBS websockets authenticated" + document.getElementById("info").innerHTML;
});
obs.on('AuthenticationFailure', (data) => function(){
document.getElementById("setup").style.display = "unset";
document.getElementById("info").innerHTML = "<br />Authentication to OBS websockets failed" + document.getElementById("info").innerHTML;
});
obs.on('ScenesChanged', (data) => function(){
scenesData = data;
updateSceneList()
});
// You must add this handler to avoid uncaught exceptions.
obs.on('error', err => {
document.getElementById("info").innerHTML = "<br />OBS websockets error" + document.getElementById("info").innerHTML;
console.error('socket error:', err);
if ("error" in err){
document.getElementById("info").innerHTML = "<br />"+err.error + document.getElementById("info").innerHTML;
}
});
</script>
</body>
</html>

69
examples/p2p.html Normal file
View File

@ -0,0 +1,69 @@
<html>
<body>
<div id="results" style="overflow:scroll;max-height:300px;">
starting...
</div>
<script> // https://jsfiddle.net/steveseguin/0t3ayvk8/31/
var connectionID = Math.random()*100000001;
function RecvDataWindow(){
var iframe = document.createElement("iframe");
iframe.src = "https://vdo.ninja/beta/?view="+connectionID+"&cleanoutput";
iframe.style.width = "0px";
iframe.style.height = "0px";
iframe.style.position = "fixed";
iframe.style.left = "-100px";
iframe.style.top = "-100px";
iframe.id = "frame1"
document.body.appendChild(iframe);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if ("dataReceived" in e.data){ // raw data
document.getElementById("results").innerHTML += e.data.dataReceived+"<br />";
console.log(e.data);
try {
iframe.contentWindow.postMessage({"sendData":"pong!!", "UUID":e.data.UUID}, '*');
} catch(E){}
}
});
}
function SendDataWindow(){
var iframe = document.createElement("iframe");
iframe.src = "https://vdo.ninja/beta/?push="+connectionID+"&vd=0&ad=0&autostart&cleanoutput";
iframe.style.width = "0px";
iframe.style.height = "0px";
iframe.style.position = "fixed";
iframe.style.left = "-100px";
iframe.style.top = "-100px";
iframe.id = "frame2";
document.body.appendChild(iframe);
setInterval(function(){
try {
console.log(".");
document.getElementById("frame2").contentWindow.postMessage({"sendData":"ping!!"}, '*');
} catch(E){}
}, 1000);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if ("dataReceived" in e.data){ // raw data
console.log(e.data);
document.getElementById("results").innerHTML += e.data.dataReceived+"<br />";
}
});
}
SendDataWindow();
RecvDataWindow();
</script>
</body>
</html>

23
examples/readme.md Normal file
View File

@ -0,0 +1,23 @@
p2p is a sample of how to use vdo.ninja as a data transport tunneling service
twitch is an example of how to have a twitch live chat side-by-side with VDO.NInja on the same screen
dual is an example of how to have two VDO.Ninja windows (or any windows really) open on the same page; Picture-in-Picture style
sensors is an example of how to transmit sensor and video data from a phone to a computer, drawing it to canvas: youtube video for this exists
midi demonstrates the MIDI API for VDO.Ninja
draggable demonstrates how to drag multiple windows around, if you wanted to create a custom layout of elements. (experimental)
chat.html is an example of a chat-only interface for VDO.NInja; maybe dockable into OBS even
iframe.outbound-stats.html demostrates how to get stats from VDO.Ninja using the IFRAME API
changepass lets you create passwords and related HASH values for VDO.NInja rooms
webhid demonstrates how to interface with a USB device, like a streamdeck (mouse/keyboard not supported)
zoom.html is a tool for letting you publish into VDO.Ninja, but then full-screen the window once setup, allowing for window-capturing into zoom.
obs_remote is also hosted on github elsewhere, but it's an example of how to remotely control OBS using VDO.Ninja's tunneling abilities

280
examples/sensors.html Normal file
View File

@ -0,0 +1,280 @@
<html>
<head><title>Sensor and video stream access example</title>
<style>
body{
padding:0;
margin:0;
background-color: rgb(222,242,253);
}
iframe {
border:0;
margin:0;
padding:0;
display:block;
margin:10px;
width:640px;
height:320px;
}
#viewlink {
width:400px;
}
#container {
display:block;
padding:0px;
}
input{
padding:5px;
margin:5px;
}
button{
padding:5px;
margin:5px;
}
canvas{
width:100%;
display:block;
margin:0;
padding:0;
}
</style>
</head>
<body>
<canvas id="canvas" style="display:none;max-height:70vh;max-width:calc(70vh*1.777);width:100%;height:100%;" width="1920" height="1080" ></canvas>
<input placeholder="Enter a VDO.Ninja View URL here" id="viewlink" style="display:block;" onchange="loadIframe();"/>
<label for="hori">FOA-Horizontal</label>
<input type="range" id="hori" name="hori" value="63" title="63" min="40" max="80" title="67" onchange="updateHor(this);">
<label for="vert">FOA-Vertical</label>
<input type="range" id="vert" name="vert" value="50" title="50" min="30" max="70" onchange="updateVer(this);">
<label for="draw">Draw Delay</label>
<input type="range" id="draw" name="draw" value="110" title="110" min="0" max="500" style="width:500px" onchange="updateDelay(this);"><br /><br />
Add &sensor to the push link to send data; see: <a target="_blank" href="https://docs.vdo.ninja/source-settings/sensor">https://docs.vdo.ninja/source-settings/sensor</a>
<div id="container">
</div>
<script>
// https://www.camerafv5.com/devices/manufacturers/google/pixel_4a_sunfish_1/ ; pixel 4a specs
var horFOA = 49.6;
var verFOA = 63.3;
var drawDelay = 110;
function updateHor(hor){
horFOA = parseInt(hor.value);
hor.title = horFOA;
}
function updateVer(ver){
verFOA = parseInt(ver.value);
ver.title = verFOA;
}
function updateDelay(time){
drawDelay = parseInt(time.value);
time.title = drawDelay;
}
function loadIframe(url=false){ // this is pretty important if you want to avoid camera permission popup problems. You can also call it automatically via: <body onload=>loadIframe();"> , but don't call it before the page loads.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled= false;
var iframe = document.createElement("iframe");
var iframeContainer = document.createElement("div");
if (url){
var iframesrc = url;
} else {
var iframesrc = document.getElementById("viewlink").value;
}
console.log(iframesrc);
document.getElementById("viewlink").parentNode.removeChild(document.getElementById("viewlink"));
document.getElementById("canvas").style.display="block";
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
if (iframesrc==""){
iframesrc="./";
}
iframe.src = iframesrc;
iframeContainer.appendChild(iframe);
document.getElementById("container").appendChild(iframeContainer);
var videos = iframe.contentWindow.document.querySelectorAll("video");
var sensors = {};
function drawFrame(vid){
try {
if (sensors.mag){ // androids may not support this.
var angle = 1.5 * Math.PI - Math.atan2(sensors.mag.y,sensors.mag.x);
var startPixel = (angle / ( 2 * Math.PI)) * 1920;
var endPixel = (verFOA/360) * 1920 + startPixel;
} else if (sensors.ori){
var angle = sensors.ori.a;
var frontToBack = sensors.ori.b;
var leftToRight = sensors.ori.g;
var startPixel = Math.floor((angle / 360) * 1920);
var width = Math.floor((verFOA/360) * 1920);
var height = vid.videoHeight*(width/vid.videoWidth);
var h_offset = Math.floor(((frontToBack+(verFOA/2))/180 * 1080)-540);
var w_offset = Math.floor((leftToRight+horFOA)/180 * 1920);
}
setTimeout(function(a1,a2,a3,a4,a5){
ctx.filter = 'blur(4px)';
ctx.drawImage(a1,a2,a3,a4,a5);
ctx.filter = "none";
ctx.drawImage(a1,a2,a3,a4,a5);
}, drawDelay, vid, startPixel-w_offset, h_offset, width, height);
} catch(e){console.error(e);}
};
setInterval(function(){
if (videos.length){
if ("UUID" in sensors){
if (videos[0].id !== "videosource_"+sensors.UUID){
videos = iframe.contentWindow.document.querySelectorAll("video#videosource_"+sensors.UUID);
}
if (videos.length){
drawFrame(videos[0]);
}
}
}
},100);
//////////// LISTEN FOR EVENTS
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if ("stats" in e.data){
var outputWindow = document.createElement("div");
//console.log(e.data.stats);
var out = "<br />total_inbound_connections:"+e.data.stats.total_inbound_connections;
out += "<br />total_outbound_connections:"+e.data.stats.total_outbound_connections;
for (var streamID in e.data.stats.inbound_stats){
out += "<br /><br /><b>streamID:</b> "+streamID+"<br />";
out += printValues(e.data.stats.inbound_stats[streamID]);
}
outputWindow.innerHTML = out;
iframeContainer.appendChild(outputWindow);
}
if ("gotChat" in e.data){
var outputWindow = document.createElement("div");
outputWindow.innerHTML = e.data.gotChat.msg;
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
}
if ("action" in e.data){
var outputWindow = document.createElement("div");
outputWindow.innerHTML = "child-page-action: "+e.data.action+"<br />";
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
console.log(e.data.action);
if (e.data.action == "new-view-connection"){
setTimeout(function(){
videos = iframe.contentWindow.document.querySelectorAll("video");
console.log(videos);
},500);
}
}
if ("streamIDs" in e.data){
var outputWindow = document.createElement("div");
outputWindow.innerHTML = "child-page-action: streamIDs<br />";
for (var key in e.data.streamIDs) {
outputWindow.innerHTML += "streamID: " + key + ", label:"+e.data.streamIDs[key] + "\n";
}
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
}
if ("loudness" in e.data){
//console.log(e.data);
if (document.getElementById("loudness")){
outputWindow = document.getElementById("loudness");
} else {
var outputWindow = document.createElement("div");
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
outputWindow.id = "loudness";
}
outputWindow.innerHTML = "child-page-action: loudness<br />";
for (var key in e.data.loudness) {
outputWindow.innerHTML += key + " Loudness: " + e.data.loudness[key] + "\n";
}
outputWindow.style.border="1px black";
}
if ("sensors" in e.data){
sensors = e.data.sensors;
if (document.getElementById("sensors")){
outputWindow = document.getElementById("sensors");
} else {
var outputWindow = document.createElement("div");
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
outputWindow.id = "sensors";
console.log(sensors);
}
outputWindow.innerHTML = "child-page-action: sensors<br /><br />";
for (var key in e.data.sensors.lin) {
outputWindow.innerHTML += key + " linear: " + e.data.sensors.lin[key] + "<br />";
}
for (var key in e.data.sensors.acc) {
outputWindow.innerHTML += key + " acceleration: " + e.data.sensors.acc[key] + "<br />";
}
for (var key in e.data.sensors.gyro) {
outputWindow.innerHTML += key + " gyro: " + e.data.sensors.gyro[key] + "<br />";
}
for (var key in e.data.sensors.mag) {
outputWindow.innerHTML += key + " magnet: " + e.data.sensors.mag[key] + "<br />";
}
for (var key in e.data.sensors.ori) {
outputWindow.innerHTML += key + " orientation: " + e.data.sensors.ori[key] + "<br />";
}
outputWindow.style.border="1px black";
}
});
}
function printValues( obj) {
var out = "";
for (var key in obj) {
if (typeof obj[key] === "object") {
out +="<br />";
out += printValues(obj[key]);
} else {
out +="<b>"+key+"</b>: "+obj[key]+"<br />";
}
}
return out;
}
</script>
</body>
</html>

111
examples/twitch.html Normal file
View File

@ -0,0 +1,111 @@
<html>
<head><title>Twitch + Video</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=1.0, user-scalable=yes" />
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<style>
body{
padding:0;
margin:0;
background-color:#003;
width:100%;
height:100%;
}
iframe {
width:100%;
height:100%;
border:0;
margin:0;
padding:0;
position:absolute;
display:block;
}
input{
padding:10px;
width:80%;
font-size:1.2em;
z-index: 1000;
}
@media screen and (orientation:portrait) {
#container2{
width:100%;height:100%;display:none;
}
#container1{
width: 50vw;height: 50vh; display:none; float:left; position: fixed; top: 0; right: 0%;
}
iframe{
width:100%;
}
}
@media screen and (orientation:landscape) {
#container2{
width:60vw;height:100%;display:none;
z-index:5;
}
#container1{
width: 50vw;height: 80vh; display:none; float:left; position: fixed; top: 0; right: -10vw;
}
iframe{
max-width:60vw;
}
}
</style>
</head>
<body>
<div id="container2"></div>
<div id="container1" ></div>
<div id="clean">
<input placeholder="Enter a VDON stream ID" id="viewlink" type="text" />
<input placeholder="Enter the Twitch channel name" id="twitch" type="text" />
<button onclick="loadIframes()" style="display:block;padding:10px;margin:10px;">START</button>
</div>
<script>
window.addEventListener("orientationchange", function() {
// Announce the new orientation number
// alert(window.orientation);
}, false);
function loadIframes(url=false){
var roomname = document.getElementById("viewlink").value;
var twitch = document.getElementById("twitch").value;
document.getElementById("clean").parentNode.removeChild(document.getElementById("clean"));
document.getElementById("container1").style.display="inline-block";
document.getElementById("container2").style.display="inline-block";
var path = window.location.host+window.location.pathname.split("/").slice(0,-1).join("/");
var room1 = "https://"+path+"/?push="+roomname+"&webcam&autostart&vd=front&ad=1&transparent&noheader";
var room2 = "https://www.twitch.tv/embed/"+twitch+"/chat?parent="+location.hostname;
var iframe = document.createElement("iframe");
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
iframe.src = room1;
var iframeContainer = document.createElement("div");
iframeContainer.appendChild(iframe);
document.getElementById("container1").appendChild(iframeContainer);
setTimeout(function(){
var iframe = document.createElement("iframe");
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
iframe.src = room2;
var iframeContainer = document.createElement("div");
iframeContainer.appendChild(iframe);
document.getElementById("container2").appendChild(iframeContainer);
},3000);
}
</script>
</body>
</html>

143
filters/anon.js Normal file
View File

@ -0,0 +1,143 @@
function effectsEngine(effectName){
function loadScript(url){
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = function(){
this.remove();
if (loadList.length){
loadScript(loadList.pop());
}
}
document.head.appendChild(script);
}
var loadList = [];
if (typeof JEELIZFACEFILTER == 'undefined' || JEELIZFACEFILTER==null){
loadList.push("./thirdparty/jeeliz/jeelizFaceFilter.js");
}
if (typeof THREE == 'undefined' || THREE == null){
loadList.push("./thirdparty/jeeliz/three/v112/three.min.js");
} else {
console.log("typeof THREE:"+typeof THREE);
}
if (typeof JeelizThreeHelper == 'undefined' || JeelizThreeHelper==null){
loadList.push("./thirdparty/jeeliz/JeelizThreeHelper.js");
}
if (typeof TWEEN == 'undefined' || TWEEN == null){
loadList.push("./thirdparty/jeeliz/Tween.min.js");
}
if (loadList.length){
loadList.reverse();
loadScript(loadList.pop());
}
// some globals:
let THREECAMERA = null; // should be prop of window
let ANONYMOUSMESH = null;
let ANONYMOUSOBJ3D = null;
let isTransformed = false;
var pathname = window.location.pathname.split("/");
pathname.pop();
pathname = window.location.protocol + "//" + window.location.host + pathname.join("/");
// callback: launched if a face is detected or lost.
function detect_callback(isDetected) {
// if (isDetected) {
// console.log('INFO in detect_callback(): DETECTED');
// } else {
// console.log('INFO in detect_callback(): LOST');
// }
}
// entry point:
function main(){
if (session.canvasSource && document.getElementById("effectsCanvasTarget") && JEELIZFACEFILTER){
try {
warnlog("LOADING JEELIZ");
THREECAMERA = null; // should be prop of window
ANONYMOUSMESH = null;
ANONYMOUSOBJ3D = null;
isTransformed = false;
init_faceFilter("effectsCanvasTarget", session.canvasSource);
} catch(e){
}
} else {
setTimeout(function(){main();},500);
warnlog("...retrying to load");
}
}
function init_faceFilter(canvasId, videoElement){
JEELIZFACEFILTER.init({
canvasId: canvasId,
NNCPath: pathname+'/thirdparty/jeeliz/neuralNets/',
videoSettings: {
videoElement: videoElement
},
callbackReady: function (errCode, spec) {
if (errCode) {
errorlog(errCode);
try{
JEELIZFACEFILTER.destroy();
} catch(e){}
THREECAMERA = null; // should be prop of window
ANONYMOUSMESH = null;
ANONYMOUSOBJ3D = null;
isTransformed = false;
setTimeout(function(){main();},500);
return;
}
const threeStuffs = JeelizThreeHelper.init(spec, detect_callback);
// CREATE OUR ANONYMOUS MASK:
const headLoader = new THREE.BufferGeometryLoader();
headLoader.load('./filters/anon/anonymous.json',(geometryHead) => {
const mat = new THREE.MeshLambertMaterial({
map: new THREE.TextureLoader().load('./filters/anon/anonymous.png'),
transparent: true
});
ANONYMOUSMESH = new THREE.Mesh(geometryHead, mat);
ANONYMOUSMESH.frustumCulled = false;
ANONYMOUSMESH.scale.multiplyScalar(0.065); // mask scale
ANONYMOUSMESH.position.fromArray([0, -0.75, 0.35]); // maskPositionOffset
ANONYMOUSMESH.renderOrder = 1000000;
ANONYMOUSMESH.material.opacity = 0;
ANONYMOUSOBJ3D = new THREE.Object3D();
ANONYMOUSOBJ3D.add(ANONYMOUSMESH);
threeStuffs.faceObject.add(ANONYMOUSOBJ3D);
});
THREECAMERA = JeelizThreeHelper.create_camera();
const ambient = new THREE.AmbientLight(0xffffff, 0.8);
threeStuffs.scene.add(ambient);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
dirLight.position.set(100, 1000, 1000);
threeStuffs.scene.add(dirLight);
},
callbackTrack: function (detectState) {
if (effectName !== session.effects){
try{
JEELIZFACEFILTER.toggle_pause(true,false); // unload the filter when no longer active. Leaving the track active is required, else it breaks the app
} catch(e){errorlog(e);}
THREECAMERA = null; // should be prop of window
ANONYMOUSMESH = null;
ANONYMOUSOBJ3D = null;
isTransformed = false;
return;
}
const isDetected = JeelizThreeHelper.get_isDetected();
//if (isDetected && detectState.expressions[0] >= 0.8 && !isTransformed) { // If the person opens their mouth wide, then activate..
if (isDetected && !isTransformed){
isTransformed = true;
new TWEEN.Tween( ANONYMOUSMESH.material ).to({ opacity: 1}, 700).start(); // animation
}
TWEEN.update();
JeelizThreeHelper.render(detectState, THREECAMERA);
}
});
}
return main;
}

View File

@ -0,0 +1,75 @@
function addVideoRecordingEffect(canvas) {
var viewWidth,
viewHeight,
canvas = document.getElementById("canvasVideoEffect"),
ctx;
// change these settings
var patternSize = 64,
patternScaleX = 3,
patternScaleY = 1,
patternRefreshInterval = 8,
patternAlpha = 25; // int between 0 and 255,
var patternPixelDataLength = patternSize * patternSize * 4,
patternCanvas,
patternCtx,
patternData,
frame = 0;
// create a canvas which will render the grain
function initCanvas() {
viewWidth = canvas.width = canvas.clientWidth;
viewHeight = canvas.height = canvas.clientHeight;
ctx = canvas.getContext('2d');
ctx.scale(patternScaleX, patternScaleY);
}
// create a canvas which will be used as a pattern
function initGrain() {
patternCanvas = document.createElement('canvas');
patternCanvas.width = patternSize;
patternCanvas.height = patternSize;
patternCtx = patternCanvas.getContext('2d');
patternData = patternCtx.createImageData(patternSize, patternSize);
}
// put a random shade of gray into every pixel of the pattern
function update() {
var value;
for (var i = 0; i < patternPixelDataLength; i += 1) {
value = (Math.random() * 155) | 0;
patternData.data[i ] = value;
patternData.data[i + 10] = value;
patternData.data[i + 15] = value;
patternData.data[i + 11] = patternAlpha;
}
patternCtx.putImageData(patternData, 0, 0);
}
// fill the canvas using the pattern
function draw() {
ctx.clearRect(0, 0, viewWidth, viewHeight);
ctx.fillStyle = ctx.createPattern(patternCanvas, 'repeat');
ctx.fillRect(0, 0, viewWidth, viewHeight);
}
function loop() {
if (++frame % patternRefreshInterval === 0) {
update();
draw();
}
requestAnimationFrame(loop);
}
initCanvas();
initGrain();
requestAnimationFrame(loop);
}

File diff suppressed because one or more lines are too long

BIN
filters/anon/anonymous.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

BIN
filters/anon/frame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 KiB

65
filters/cube.js Normal file
View File

@ -0,0 +1,65 @@
function effectsEngine(){
console.log('LOADED SAMPLE');
function loadScript(url, callback=false){
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = function(){this.remove();if(callback){callback();}}
document.head.appendChild(script);
}
loadScript("https://stevesserver.com/dist/jeelizFaceFilter.js",
function(){
loadScript("https://stevesserver.com/helpers/JeelizCanvas2DHelper.js");
}
);
function main(){ // entry point
console.log(".");
if (session.canvasSource && session.canvasSource.videoWidth && session.canvasSource.videoHeight && session.canvasWebGL){
if (session.canvasWebGL && !(document.getElementById("effectsCanvasTarget"))){
session.canvasWebGL.id = "effectsCanvasTarget";
session.canvasWebGL.style.position="fixed";
session.canvasWebGL.style.top= "-9999px";
session.canvasWebGL.style.left= "-9999px";
document.body.appendChild(session.canvasWebGL);
}
start(JEELIZFACEFILTER, "effectsCanvasTarget", session.canvasSource, 'yellow');
} else {
setTimeout(main, 500);
}
}
function start(jeeFaceFilterAPIInstance, canvasId, videoElement, borderColor){
let cvd = null; // return of Canvas2DDisplay
jeeFaceFilterAPIInstance.init({
canvasId: canvasId,
videoSettings: {
videoElement: videoElement
},
NNCPath: 'https://stevesserver.com/neuralNets/', // root of NN_DEFAULT.json file
callbackReady: function(errCode, spec){
if (errCode){
console.log('AN ERROR HAPPENS. SORRY BRO :( . ERR =', errCode);
return;
}
console.log('INFO: JEELIZFACEFILTER IS READY');
cvd = JeelizCanvas2DHelper(spec);
cvd.ctx.strokeStyle = borderColor;
},
callbackTrack: function(detectState){ // drawing loop
if (detectState.detected>0.6){
// draw a border around the face:
const faceCoo = cvd.getCoordinates(detectState);
cvd.ctx.clearRect(0,0,cvd.canvas.width, cvd.canvas.height);
cvd.ctx.strokeRect(faceCoo.x, faceCoo.y, faceCoo.w, faceCoo.h);
cvd.update_canvasTexture();
}
cvd.draw();
}
});
}
main();
};

357
filters/dog.js Normal file
View File

@ -0,0 +1,357 @@
function effectsEngine(effectName){
function loadScript(url){
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = function(){
this.remove();
if (loadList.length){
loadScript(loadList.pop());
}
}
document.head.appendChild(script);
}
var loadList = [];
if (typeof JEELIZFACEFILTER == 'undefined' || JEELIZFACEFILTER==null){
loadList.push("./thirdparty/jeeliz/jeelizFaceFilter.js");
}
if (typeof THREE == 'undefined' || THREE == null){
loadList.push("./thirdparty/jeeliz/three/v112/three.min.js");
} else {
console.log("typeof THREE:"+typeof THREE);
}
if (typeof JeelizThreeHelper == 'undefined' || JeelizThreeHelper==null){
loadList.push("./thirdparty/jeeliz/JeelizThreeHelper.js");
}
if (typeof TWEEN == 'undefined' || TWEEN == null){
loadList.push("./thirdparty/jeeliz/Tween.min.js");
}
loadList.push("./filters/dog/libs/glfx.js");
loadList.push("./thirdparty/jeeliz/three/customMaterials/FlexMaterial/ThreeFlexMaterial.js");
if (loadList.length){
loadList.reverse();
loadScript(loadList.pop());
}
var pathname = window.location.pathname.split("/");
pathname.pop();
pathname = window.location.protocol + "//" + window.location.host + pathname.join("/");
// some globals:
let THREECAMERA = null; // should be prop of window
let isTransformed = false;
let ISDETECTED = false;
let NOSEMESH = null, EARMESH = null;
let DOGOBJ3D = null, FRAMEOBJ3D = null;
let ISOVERTHRESHOLD = false, ISUNDERTRESHOLD = true;
let ISLOADED = false;
let MIXER = null;
let ACTION = null;
let ISANIMATING = false;
let ISOPAQUE = false;
let ISANIMATIONOVER = false;
let _flexParts = [];
let _videoGeometry = null;
// callback: launched if a face is detected or lost.
function detect_callback(isDetected) {
// if (isDetected) {
// console.log('INFO in detect_callback(): DETECTED');
// } else {
// console.log('INFO in detect_callback(): LOST');
// }
}
function create_mat2d(threeTexture, isTransparent){ // MT216: we put the creation of the video material in a func because we will also use it for the frame
return new THREE.RawShaderMaterial({
depthWrite: false,
depthTest: false,
transparent: isTransparent,
vertexShader: "attribute vec2 position;\n\
varying vec2 vUV;\n\
void main(void){\n\
gl_Position = vec4(position, 0., 1.);\n\
vUV = 0.5 + 0.5 * position;\n\
}",
fragmentShader: "precision lowp float;\n\
uniform sampler2D samplerVideo;\n\
varying vec2 vUV;\n\
void main(void){\n\
gl_FragColor = texture2D(samplerVideo, vUV);\n\
}",
uniforms:{
samplerVideo: { value: threeTexture }
}
});
}
function applyFilter() {
let canvas;
try {
canvas = fx.canvas();
} catch (e) {
alert('Ow no! WebGL isn\'t supported...')
return
}
const tempImage = new Image(512, 512);
tempImage.src = './filters/dog/images/texture_pink.jpg';
tempImage.onload = () => {
const texture = canvas.texture(tempImage);
// Create the effet
canvas.draw(texture).vignette(0.5, 0.6).update();
const canvasOpacity = document.createElement('canvas');
canvasOpacity.width = 512;
canvasOpacity.height = 512;
const ctx = canvasOpacity.getContext('2d');
ctx.globalAlpha = 0.2
ctx.drawImage(canvas, 0, 0, 512, 512);
// Add the effect
const calqueMesh = new THREE.Mesh(_videoGeometry, create_mat2d(new THREE.TextureLoader().load(canvasOpacity.toDataURL('image/png')), true))
calqueMesh.material.opacity = 0.2;
calqueMesh.material.transparent = true;
calqueMesh.renderOrder = 999; // render last
calqueMesh.frustumCulled = false;
FRAMEOBJ3D.add(calqueMesh);
}
}
// build the 3D. called once when Jeeliz Face Filter is OK
function init_threeScene(spec) {
// INIT THE THREE.JS context
const threeStuffs = JeelizThreeHelper.init(spec, detect_callback);
_videoGeometry = threeStuffs.videoMesh.geometry;
// CREATE OUR DOG EARS:
// let's begin by creating a loading manager that will allow us to
// have more control over the three parts of our dog model
const loadingManager = new THREE.LoadingManager();
const loaderEars = new THREE.BufferGeometryLoader(loadingManager);
loaderEars.load(
'./filters/dog/models/dog/dog_ears.json',
function (geometry) {
const mat = new THREE.FlexMaterial({
map: new THREE.TextureLoader().load('./filters/dog/models/dog/texture_ears.jpg'),
flexMap: new THREE.TextureLoader().load('./filters/dog/models/dog/flex_ears_256.jpg'),
alphaMap: new THREE.TextureLoader().load('./filters/dog/models/dog/alpha_ears_256.jpg'),
transparent: true,
opacity: 1,
bumpMap: new THREE.TextureLoader().load('./filters/dog/models/dog/normal_ears.jpg'),
bumpScale: 0.0075,
shininess: 1.5,
specular: 0xffffff,
});
EARMESH = new THREE.Mesh(geometry, mat);
EARMESH.scale.multiplyScalar(0.025);
EARMESH.position.setY(-0.3);
EARMESH.frustumCulled = false;
EARMESH.renderOrder = 10000;
EARMESH.material.opacity.value = 1;
}
);
// CREATE OUR DOG NOSE
const loaderNose = new THREE.BufferGeometryLoader(loadingManager);
loaderNose.load(
'./filters/dog/models/dog/dog_nose.json',
function (geometry) {
const mat = new THREE.MeshPhongMaterial({
map: new THREE.TextureLoader().load('./filters/dog/models/dog/texture_nose.jpg'),
shininess: 1.5,
specular: 0xffffff,
bumpMap: new THREE.TextureLoader().load('./filters/dog/models/dog/normal_nose.jpg'),
bumpScale: 0.005
});
NOSEMESH = new THREE.Mesh(geometry, mat);
NOSEMESH.scale.multiplyScalar(0.018);
NOSEMESH.position.setY(-0.05);
NOSEMESH.position.setZ(0.15);
NOSEMESH.frustumCulled = false;
NOSEMESH.renderOrder = 10000;
}
);
loadingManager.onLoad = () => {
DOGOBJ3D.add(EARMESH);
DOGOBJ3D.add(NOSEMESH);
threeStuffs.faceObject.add(DOGOBJ3D);
ISLOADED = true;
}
// CREATE AN AMBIENT LIGHT
const ambient = new THREE.AmbientLight(0xffffff, 0.8);
threeStuffs.scene.add(ambient);
// CREAT A DIRECTIONALLIGHT
const dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
dirLight.position.set(100, 1000, 1000);
threeStuffs.scene.add(dirLight);
// CREATE THE CAMERA
THREECAMERA = JeelizThreeHelper.create_camera();
threeStuffs.scene.add(FRAMEOBJ3D);
// Add filter
applyFilter();
} // end init_threeScene()
function animateTongue (mesh, isReverse) {
mesh.visible = true;
if (isReverse) {
ACTION.timescale = -1;
ACTION.paused = false;
setTimeout(() => {
ACTION.paused = true;
ISOPAQUE = false;
ISANIMATING = false;
ISANIMATIONOVER = true;
new TWEEN.Tween(mesh.material.opacity)
.to({ value: 0 }, 150)
.start();
}, 150);
} else {
ACTION.timescale = 1;
ACTION.reset();
ACTION.paused = false;
new TWEEN.Tween(mesh.material.opacity)
.to({ value: 1 }, 100)
.onComplete(() => {
ISOPAQUE = true;
setTimeout(() => {
ACTION.paused = true;
ISANIMATING = false;
ISANIMATIONOVER = true;
}, 150);
})
.start();
}
}
// entry point:
function main(){
if (session.canvasSource && document.getElementById("effectsCanvasTarget") && JEELIZFACEFILTER){
try {
warnlog("LOADING JEELIZ");
THREECAMERA = null; // should be prop of window
isTransformed = false;
DOGOBJ3D = new THREE.Object3D();
FRAMEOBJ3D = new THREE.Object3D();
init_faceFilter("effectsCanvasTarget", session.canvasSource);
} catch(e){
}
} else {
setTimeout(function(){main();},500);
warnlog("...retrying to load");
}
}
function init_faceFilter(canvasId, videoElement){
JEELIZFACEFILTER.init({
canvasId: canvasId,
NNCPath: pathname+'/thirdparty/jeeliz/neuralNets/',
videoSettings: {
videoElement: videoElement
},
callbackReady: function (errCode, spec) {
if (errCode) {
errorlog(errCode);
try{
JEELIZFACEFILTER.destroy();
} catch(e){}
THREECAMERA = null; // should be prop of window
DOGOBJ3D=null;
FRAMEOBJ3D=null;
isTransformed = false;
setTimeout(function(){main();},500);
return;
}
init_threeScene(spec);
},
callbackTrack: function (detectState) {
if (effectName !== session.effects){
try{
JEELIZFACEFILTER.toggle_pause(true,false); // unload the filter when no longer active. Leaving the track active is required, else it breaks the app
} catch(e){errorlog(e);}
THREECAMERA = null; // should be prop of window
isTransformed = false;
DOGOBJ3D=null;
FRAMEOBJ3D=null;
return;
}
const ISDETECTED = JeelizThreeHelper.get_isDetected();
//if (isDetected && detectState.expressions[0] >= 0.8 && !isTransformed) { // If the person opens their mouth wide, then activate..
if (ISDETECTED){
const _quat = new THREE.Quaternion();
const _eul = new THREE.Euler();
_eul.setFromQuaternion(_quat);
// flex ears material:
if (EARMESH && EARMESH.material.set_amortized){
EARMESH.material.set_amortized(
EARMESH.getWorldPosition(new THREE.Vector3(0,0,0)),
EARMESH.getWorldScale(new THREE.Vector3(0,0,0)),
EARMESH.getWorldQuaternion(_eul),
false,
0.1
);
}
if (detectState.expressions[0] >= 0.85 && !ISOVERTHRESHOLD) {
ISOVERTHRESHOLD = true;
ISUNDERTRESHOLD = false;
ISANIMATIONOVER = false;
}
if (detectState.expressions[0] <= 0.1 && !ISUNDERTRESHOLD) {
ISOVERTHRESHOLD = false;
ISUNDERTRESHOLD = true;
ISANIMATIONOVER = false;
}
}
TWEEN.update();
if (ISOPAQUE) {
MIXER.update(0.16);
}
JeelizThreeHelper.render(detectState, THREECAMERA);
}
});
}
return main;
}

View File

@ -0,0 +1,132 @@
"use strict";
THREE.FlexMaterial = function(spec){
const _worldMatrixDelayed = new THREE['Matrix4']();
function mix(a,b,t){
a.set(
b.x*t+a.x*(1-t),
b.y*t+a.y*(1-t),
b.z*t+a.z*(1-t)
);
}
// tweak shaders helpers:
function tweak_shaderAdd(code, chunk, glslCode){
return code.replace(chunk, chunk+"\n"+glslCode);
}
function tweak_shaderDel(code, chunk){
return code.replace(chunk, '');
}
function tweak_shaderRepl(code, chunk, glslCode){
return code.replace(chunk, glslCode);
}
// get PHONG shader and tweak it :
const phongShader = THREE.ShaderLib.phong;
let vertexShaderSource = phongShader.vertexShader;
vertexShaderSource = tweak_shaderAdd(vertexShaderSource, '#include <common>',
'uniform mat4 modelMatrixDelayed;\n'
+'uniform sampler2D flexMap;\n'
);
vertexShaderSource = tweak_shaderDel(vertexShaderSource, '#include <worldpos_vertex>');
vertexShaderSource = tweak_shaderRepl(vertexShaderSource, '#include <project_vertex>',
"vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\
vec4 worldPositionDelayed = modelMatrixDelayed * vec4( transformed, 1.0 );\n\
worldPosition = mix(worldPosition, worldPositionDelayed, texture2D(flexMap, uv).r);\n\
vec4 mvPosition = viewMatrix* worldPosition;\n\
gl_Position = projectionMatrix * mvPosition;");
const uniforms0 = {
'modelMatrixDelayed': {
'value': _worldMatrixDelayed
},
'flexMap': {
value: spec.flexMap
},
'opacity': {
value: (typeof(spec.opacity)!=='undefined')?spec.opacity:1
}
};
const uniforms = Object.assign({}, phongShader.uniforms, uniforms0);
const isMorphs = (spec.morphTargets) ? true : false;
const mat = new THREE.ShaderMaterial({
vertexShader: vertexShaderSource,
fragmentShader: phongShader.fragmentShader,
uniforms: uniforms,
transparent: (spec.transparent)?true:false,
lights: true,
morphTargets: isMorphs,
morphNormals: isMorphs
});
mat.flexMap = spec.flexMap;
mat.opacity = mat.uniforms.opacity; // shortcut
if (typeof(spec.map)!=='undefined') {
uniforms.map = {value: spec.map};
mat.map = spec.map;
}
if (typeof(spec.alphaMap)!=='undefined') {
uniforms.alphaMap = {value: spec.alphaMap};
mat.transparent = true;
mat.alphaMap = spec.alphaMap;
}
if (typeof(spec.bumpMap)!=='undefined') {
uniforms.bumpMap = {value: spec.bumpMap};
mat.bumpMap = spec.bumpMap;
}
if (typeof(spec.bumpScale)!=='undefined') {
uniforms.bumpScale = {value: spec.bumpScale};
mat.bumpScale = spec.bumpScale;
}
if (typeof(spec.shininess)!=='undefined') {
uniforms.shininess = {value: spec.shininess};
mat.shininess = spec.shininess;
}
const _positionDelayed = new THREE.Vector3();
const _scaleDelayed = new THREE.Vector3();
const _eulerDelayed = new THREE['Euler']();
let _initialized = false;
mat.set_amortized = function(positionTarget, scaleTarget, eulerTarget, parentMatrix, amortization){
if (!_initialized){
if (positionTarget){
_positionDelayed.copy(positionTarget);
}
if (scaleTarget){
_scaleDelayed.copy(scaleTarget);
}
if (eulerTarget){
_eulerDelayed.copy(eulerTarget);
}
_initialized = true;
}
if (eulerTarget){
mix( _eulerDelayed, eulerTarget, amortization );
_worldMatrixDelayed['makeRotationFromEuler'](_eulerDelayed);
}
if (positionTarget){
mix( _positionDelayed, positionTarget, amortization );
_worldMatrixDelayed['setPosition'](_positionDelayed);
}
if (scaleTarget){
mix(_scaleDelayed, scaleTarget, amortization );
_worldMatrixDelayed['scale'](_scaleDelayed);
}
if (parentMatrix){
_worldMatrixDelayed.multiplyMatrices(parentMatrix, _worldMatrixDelayed);
}
}
return mat;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

87
filters/dog/index.html Normal file
View File

@ -0,0 +1,87 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="content-language" content="en-EN" />
<title>JEELIZ FACEFILTER: DOG</title>
<!-- INCLUDE JEELIZ FACEFILTER SCRIPT -->
<script src="../../../dist/jeelizFaceFilter.js"></script>
<!-- INCLUDE THREE.JS -->
<script src="../../../libs/three/v97/three.js"></script>
<!-- INCLUDE JEELIZRESIZER -->
<script src="../../../helpers/JeelizResizer.js"></script>
<!-- INCLUDE JEELIZTHREEJSHELPER -->
<script src="../../../helpers/JeelizThreeHelper.js"></script>
<!-- INCLUDE FLEXMATERIAL (CUSTOM DEV) -->
<script src="../../../libs/three/customMaterials/FlexMaterial/ThreeFlexMaterial.js"></script>
<!-- INCLUDE TWEEN.JS -->
<script src='../../../libs/tween/v16_3_5/Tween.min.js'></script>
<!-- INCLUDE JQUERY -->
<script src='../../../libs/jquery/jquery-3.3.1.min.js'></script>
<!-- INCLUDE GLFX -->
<script src='libs/glfx.js'></script>
<!-- INCLUDE DEMO SCRIPT -->
<script src="./main.js"></script>
<!-- INCLUDE ADDDRAGEVENTLISTENER.JS -->
<script src='../../../helpers/addDragEventListener.js'></script>
<!-- INCLUDE FORK ME ON GITHUB BANNER -->
<script src="../../appearance/widget.js"></script>
<link rel="stylesheet" href="../../appearance/style.css" type="text/css" />
<style>
.canvasContainer {
position: relative;
margin: 0 auto;
text-align: center;
}
#jeeFaceFilterCanvas {
z-index: 0;
max-height: 100%;
left: auto;
top: auto;
width: 100vmin;
transform: translate(0,0) rotateY(180deg);
position: static;
}
img {
max-width: 100%;
}
#filter {
position: absolute;
z-index: 0;
max-height: 100%;
width: 100vmin;
top: 0;
left: 50%;
transform: translate(-50%);
opacity: 0.15;
}
#filter canvas {
width: 100%;
height: 100%;
}
</style>
</head>
<body onload="main()">
<div class="canvasContainer">
<canvas width="600" height="600" id='jeeFaceFilterCanvas'></canvas>
<div id='filter'></div>
</div>
</body>
</html>

59
filters/dog/libs/glfx.js Normal file
View File

@ -0,0 +1,59 @@
/*
* glfx.js
* http://evanw.github.com/glfx.js/
*
* Copyright 2011 Evan Wallace
* Released under the MIT license
*/
var fx=function(){function q(a,d,c){return Math.max(a,Math.min(d,c))}function w(b){return{_:b,loadContentsOf:function(b){a=this._.gl;this._.loadContentsOf(b)},destroy:function(){a=this._.gl;this._.destroy()}}}function A(a){return w(r.fromElement(a))}function B(b,d){var c=a.UNSIGNED_BYTE;if(a.getExtension("OES_texture_float")&&a.getExtension("OES_texture_float_linear")){var e=new r(100,100,a.RGBA,a.FLOAT);try{e.drawTo(function(){c=a.FLOAT})}catch(g){}e.destroy()}this._.texture&&this._.texture.destroy();
this._.spareTexture&&this._.spareTexture.destroy();this.width=b;this.height=d;this._.texture=new r(b,d,a.RGBA,c);this._.spareTexture=new r(b,d,a.RGBA,c);this._.extraTexture=this._.extraTexture||new r(0,0,a.RGBA,c);this._.flippedShader=this._.flippedShader||new h(null,"uniform sampler2D texture;varying vec2 texCoord;void main(){gl_FragColor=texture2D(texture,vec2(texCoord.x,1.0-texCoord.y));}");this._.isInitialized=!0}function C(a,d,c){this._.isInitialized&&
a._.width==this.width&&a._.height==this.height||B.call(this,d?d:a._.width,c?c:a._.height);a._.use();this._.texture.drawTo(function(){h.getDefaultShader().drawRect()});return this}function D(){this._.texture.use();this._.flippedShader.drawRect();return this}function f(a,d,c,e){(c||this._.texture).use();this._.spareTexture.drawTo(function(){a.uniforms(d).drawRect()});this._.spareTexture.swapWith(e||this._.texture)}function E(a){a.parentNode.insertBefore(this,a);a.parentNode.removeChild(a);return this}
function F(){var b=new r(this._.texture.width,this._.texture.height,a.RGBA,a.UNSIGNED_BYTE);this._.texture.use();b.drawTo(function(){h.getDefaultShader().drawRect()});return w(b)}function G(){var b=this._.texture.width,d=this._.texture.height,c=new Uint8Array(4*b*d);this._.texture.drawTo(function(){a.readPixels(0,0,b,d,a.RGBA,a.UNSIGNED_BYTE,c)});return c}function k(b){return function(){a=this._.gl;return b.apply(this,arguments)}}function x(a,d,c,e,g,l,n,p){var m=c-g,h=e-l,f=n-g,k=p-l;g=a-c+g-n;l=
d-e+l-p;var q=m*k-f*h,f=(g*k-f*l)/q,m=(m*l-g*h)/q;return[c-a+f*c,e-d+f*e,f,n-a+m*n,p-d+m*p,m,a,d,1]}function y(a){var d=a[0],c=a[1],e=a[2],g=a[3],l=a[4],n=a[5],p=a[6],m=a[7];a=a[8];var f=d*l*a-d*n*m-c*g*a+c*n*p+e*g*m-e*l*p;return[(l*a-n*m)/f,(e*m-c*a)/f,(c*n-e*l)/f,(n*p-g*a)/f,(d*a-e*p)/f,(e*g-d*n)/f,(g*m-l*p)/f,(c*p-d*m)/f,(d*l-c*g)/f]}function z(a){var d=a.length;this.xa=[];this.ya=[];this.u=[];this.y2=[];a.sort(function(a,b){return a[0]-b[0]});for(var c=0;c<d;c++)this.xa.push(a[c][0]),this.ya.push(a[c][1]);
this.u[0]=0;this.y2[0]=0;for(c=1;c<d-1;++c){a=this.xa[c+1]-this.xa[c-1];var e=(this.xa[c]-this.xa[c-1])/a,g=e*this.y2[c-1]+2;this.y2[c]=(e-1)/g;this.u[c]=(6*((this.ya[c+1]-this.ya[c])/(this.xa[c+1]-this.xa[c])-(this.ya[c]-this.ya[c-1])/(this.xa[c]-this.xa[c-1]))/a-e*this.u[c-1])/g}this.y2[d-1]=0;for(c=d-2;0<=c;--c)this.y2[c]=this.y2[c]*this.y2[c+1]+this.u[c]}function u(a,d){return new h(null,a+"uniform sampler2D texture;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 coord=texCoord*texSize;"+
d+"gl_FragColor=texture2D(texture,coord/texSize);vec2 clampedCoord=clamp(coord,vec2(0.0),texSize);if(coord!=clampedCoord){gl_FragColor.a*=max(0.0,1.0-length(coord-clampedCoord));}}")}function H(b,d){a.brightnessContrast=a.brightnessContrast||new h(null,"uniform sampler2D texture;uniform float brightness;uniform float contrast;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color.rgb+=brightness;if(contrast>0.0){color.rgb=(color.rgb-0.5)/(1.0-contrast)+0.5;}else{color.rgb=(color.rgb-0.5)*(1.0+contrast)+0.5;}gl_FragColor=color;}");
f.call(this,a.brightnessContrast,{brightness:q(-1,b,1),contrast:q(-1,d,1)});return this}function t(a){a=new z(a);for(var d=[],c=0;256>c;c++)d.push(q(0,Math.floor(256*a.interpolate(c/255)),255));return d}function I(b,d,c){b=t(b);1==arguments.length?d=c=b:(d=t(d),c=t(c));for(var e=[],g=0;256>g;g++)e.splice(e.length,0,b[g],d[g],c[g],255);this._.extraTexture.initFromBytes(256,1,e);this._.extraTexture.use(1);a.curves=a.curves||new h(null,"uniform sampler2D texture;uniform sampler2D map;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color.r=texture2D(map,vec2(color.r)).r;color.g=texture2D(map,vec2(color.g)).g;color.b=texture2D(map,vec2(color.b)).b;gl_FragColor=color;}");
a.curves.textures({map:1});f.call(this,a.curves,{});return this}function J(b){a.denoise=a.denoise||new h(null,"uniform sampler2D texture;uniform float exponent;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;void main(){vec4 center=texture2D(texture,texCoord);vec4 color=vec4(0.0);float total=0.0;for(float x=-4.0;x<=4.0;x+=1.0){for(float y=-4.0;y<=4.0;y+=1.0){vec4 sample=texture2D(texture,texCoord+vec2(x,y)/texSize);float weight=1.0-abs(dot(sample.rgb-center.rgb,vec3(0.25)));weight=pow(weight,exponent);color+=sample*weight;total+=weight;}}gl_FragColor=color/total;}");
for(var d=0;2>d;d++)f.call(this,a.denoise,{exponent:Math.max(0,b),texSize:[this.width,this.height]});return this}function K(b,d){a.hueSaturation=a.hueSaturation||new h(null,"uniform sampler2D texture;uniform float hue;uniform float saturation;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float angle=hue*3.14159265;float s=sin(angle),c=cos(angle);vec3 weights=(vec3(2.0*c,-sqrt(3.0)*s-c,sqrt(3.0)*s-c)+1.0)/3.0;float len=length(color.rgb);color.rgb=vec3(dot(color.rgb,weights.xyz),dot(color.rgb,weights.zxy),dot(color.rgb,weights.yzx));float average=(color.r+color.g+color.b)/3.0;if(saturation>0.0){color.rgb+=(average-color.rgb)*(1.0-1.0/(1.001-saturation));}else{color.rgb+=(average-color.rgb)*(-saturation);}gl_FragColor=color;}");
f.call(this,a.hueSaturation,{hue:q(-1,b,1),saturation:q(-1,d,1)});return this}function L(b){a.noise=a.noise||new h(null,"uniform sampler2D texture;uniform float amount;varying vec2 texCoord;float rand(vec2 co){return fract(sin(dot(co.xy,vec2(12.9898,78.233)))*43758.5453);}void main(){vec4 color=texture2D(texture,texCoord);float diff=(rand(texCoord)-0.5)*amount;color.r+=diff;color.g+=diff;color.b+=diff;gl_FragColor=color;}");
f.call(this,a.noise,{amount:q(0,b,1)});return this}function M(b){a.sepia=a.sepia||new h(null,"uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float r=color.r;float g=color.g;float b=color.b;color.r=min(1.0,(r*(1.0-(0.607*amount)))+(g*(0.769*amount))+(b*(0.189*amount)));color.g=min(1.0,(r*0.349*amount)+(g*(1.0-(0.314*amount)))+(b*0.168*amount));color.b=min(1.0,(r*0.272*amount)+(g*0.534*amount)+(b*(1.0-(0.869*amount))));gl_FragColor=color;}");
f.call(this,a.sepia,{amount:q(0,b,1)});return this}function N(b,d){a.unsharpMask=a.unsharpMask||new h(null,"uniform sampler2D blurredTexture;uniform sampler2D originalTexture;uniform float strength;uniform float threshold;varying vec2 texCoord;void main(){vec4 blurred=texture2D(blurredTexture,texCoord);vec4 original=texture2D(originalTexture,texCoord);gl_FragColor=mix(blurred,original,1.0+strength);}");
this._.extraTexture.ensureFormat(this._.texture);this._.texture.use();this._.extraTexture.drawTo(function(){h.getDefaultShader().drawRect()});this._.extraTexture.use(1);this.triangleBlur(b);a.unsharpMask.textures({originalTexture:1});f.call(this,a.unsharpMask,{strength:d});this._.extraTexture.unuse(1);return this}function O(b){a.vibrance=a.vibrance||new h(null,"uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;float mx=max(color.r,max(color.g,color.b));float amt=(mx-average)*(-amount*3.0);color.rgb=mix(color.rgb,vec3(mx),amt);gl_FragColor=color;}");
f.call(this,a.vibrance,{amount:q(-1,b,1)});return this}function P(b,d){a.vignette=a.vignette||new h(null,"uniform sampler2D texture;uniform float size;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float dist=distance(texCoord,vec2(0.5,0.5));color.rgb*=smoothstep(0.8,size*0.799,dist*(amount+size));gl_FragColor=color;}");
f.call(this,a.vignette,{size:q(0,b,1),amount:q(0,d,1)});return this}function Q(b,d,c){a.lensBlurPrePass=a.lensBlurPrePass||new h(null,"uniform sampler2D texture;uniform float power;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color=pow(color,vec4(power));gl_FragColor=vec4(color);}");var e="uniform sampler2D texture0;uniform sampler2D texture1;uniform vec2 delta0;uniform vec2 delta1;uniform float power;varying vec2 texCoord;"+
s+"vec4 sample(vec2 delta){float offset=random(vec3(delta,151.7182),0.0);vec4 color=vec4(0.0);float total=0.0;for(float t=0.0;t<=30.0;t++){float percent=(t+offset)/30.0;color+=texture2D(texture0,texCoord+delta*percent);total+=1.0;}return color/total;}";
a.lensBlur0=a.lensBlur0||new h(null,e+"void main(){gl_FragColor=sample(delta0);}");a.lensBlur1=a.lensBlur1||new h(null,e+"void main(){gl_FragColor=(sample(delta0)+sample(delta1))*0.5;}");a.lensBlur2=a.lensBlur2||(new h(null,e+"void main(){vec4 color=(sample(delta0)+2.0*texture2D(texture1,texCoord))/3.0;gl_FragColor=pow(color,vec4(power));}")).textures({texture1:1});for(var e=
[],g=0;3>g;g++){var l=c+2*g*Math.PI/3;e.push([b*Math.sin(l)/this.width,b*Math.cos(l)/this.height])}b=Math.pow(10,q(-1,d,1));f.call(this,a.lensBlurPrePass,{power:b});this._.extraTexture.ensureFormat(this._.texture);f.call(this,a.lensBlur0,{delta0:e[0]},this._.texture,this._.extraTexture);f.call(this,a.lensBlur1,{delta0:e[1],delta1:e[2]},this._.extraTexture,this._.extraTexture);f.call(this,a.lensBlur0,{delta0:e[1]});this._.extraTexture.use(1);f.call(this,a.lensBlur2,{power:1/b,delta0:e[2]});return this}
function R(b,d,c,e,g,l){a.tiltShift=a.tiltShift||new h(null,"uniform sampler2D texture;uniform float blurRadius;uniform float gradientRadius;uniform vec2 start;uniform vec2 end;uniform vec2 delta;uniform vec2 texSize;varying vec2 texCoord;"+s+"void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);vec2 normal=normalize(vec2(start.y-end.y,end.x-start.x));float radius=smoothstep(0.0,1.0,abs(dot(texCoord*texSize-start,normal))/gradientRadius)*blurRadius;for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta/texSize*percent*radius);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
var n=c-b,p=e-d,m=Math.sqrt(n*n+p*p);f.call(this,a.tiltShift,{blurRadius:g,gradientRadius:l,start:[b,d],end:[c,e],delta:[n/m,p/m],texSize:[this.width,this.height]});f.call(this,a.tiltShift,{blurRadius:g,gradientRadius:l,start:[b,d],end:[c,e],delta:[-p/m,n/m],texSize:[this.width,this.height]});return this}function S(b){a.triangleBlur=a.triangleBlur||new h(null,"uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;"+s+"void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta*percent);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
f.call(this,a.triangleBlur,{delta:[b/this.width,0]});f.call(this,a.triangleBlur,{delta:[0,b/this.height]});return this}function T(b,d,c){a.zoomBlur=a.zoomBlur||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;"+s+"void main(){vec4 color=vec4(0.0);float total=0.0;vec2 toCenter=center-texCoord*texSize;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=0.0;t<=40.0;t++){float percent=(t+offset)/40.0;float weight=4.0*(percent-percent*percent);vec4 sample=texture2D(texture,texCoord+toCenter*percent*strength/texSize);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
f.call(this,a.zoomBlur,{center:[b,d],strength:c,texSize:[this.width,this.height]});return this}function U(b,d,c,e){a.colorHalftone=a.colorHalftone||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(float angle){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);vec3 cmy=1.0-color.rgb;float k=min(cmy.x,min(cmy.y,cmy.z));cmy=(cmy-k)/(1.0-k);cmy=clamp(cmy*10.0-3.0+vec3(pattern(angle+0.26179),pattern(angle+1.30899),pattern(angle)),0.0,1.0);k=clamp(k*10.0-5.0+pattern(angle+0.78539),0.0,1.0);gl_FragColor=vec4(1.0-cmy-k,color.a);}");
f.call(this,a.colorHalftone,{center:[b,d],angle:c,scale:Math.PI/e,texSize:[this.width,this.height]});return this}function V(b,d,c,e){a.dotScreen=a.dotScreen||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;gl_FragColor=vec4(vec3(average*10.0-5.0+pattern()),color.a);}");
f.call(this,a.dotScreen,{center:[b,d],angle:c,scale:Math.PI/e,texSize:[this.width,this.height]});return this}function W(b){a.edgeWork1=a.edgeWork1||new h(null,"uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;"+s+"void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec3 sample=texture2D(texture,texCoord+delta*percent).rgb;float average=(sample.r+sample.g+sample.b)/3.0;color.x+=average*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=average*weight;total.y+=weight;}}gl_FragColor=vec4(color/total,0.0,1.0);}");
a.edgeWork2=a.edgeWork2||new h(null,"uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;"+s+"void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec2 sample=texture2D(texture,texCoord+delta*percent).xy;color.x+=sample.x*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=sample.y*weight;total.y+=weight;}}float c=clamp(10000.0*(color.y/total.y-color.x/total.x)+0.5,0.0,1.0);gl_FragColor=vec4(c,c,c,1.0);}");
f.call(this,a.edgeWork1,{delta:[b/this.width,0]});f.call(this,a.edgeWork2,{delta:[0,b/this.height]});return this}function X(b,d,c){a.hexagonalPixelate=a.hexagonalPixelate||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 tex=(texCoord*texSize-center)/scale;tex.y/=0.866025404;tex.x-=tex.y*0.5;vec2 a;if(tex.x+tex.y-floor(tex.x)-floor(tex.y)<1.0)a=vec2(floor(tex.x),floor(tex.y));else a=vec2(ceil(tex.x),ceil(tex.y));vec2 b=vec2(ceil(tex.x),floor(tex.y));vec2 c=vec2(floor(tex.x),ceil(tex.y));vec3 TEX=vec3(tex.x,tex.y,1.0-tex.x-tex.y);vec3 A=vec3(a.x,a.y,1.0-a.x-a.y);vec3 B=vec3(b.x,b.y,1.0-b.x-b.y);vec3 C=vec3(c.x,c.y,1.0-c.x-c.y);float alen=length(TEX-A);float blen=length(TEX-B);float clen=length(TEX-C);vec2 choice;if(alen<blen){if(alen<clen)choice=a;else choice=c;}else{if(blen<clen)choice=b;else choice=c;}choice.x+=choice.y*0.5;choice.y*=0.866025404;choice*=scale/texSize;gl_FragColor=texture2D(texture,choice+center/texSize);}");
f.call(this,a.hexagonalPixelate,{center:[b,d],scale:c,texSize:[this.width,this.height]});return this}function Y(b){a.ink=a.ink||new h(null,"uniform sampler2D texture;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 dx=vec2(1.0/texSize.x,0.0);vec2 dy=vec2(0.0,1.0/texSize.y);vec4 color=texture2D(texture,texCoord);float bigTotal=0.0;float smallTotal=0.0;vec3 bigAverage=vec3(0.0);vec3 smallAverage=vec3(0.0);for(float x=-2.0;x<=2.0;x+=1.0){for(float y=-2.0;y<=2.0;y+=1.0){vec3 sample=texture2D(texture,texCoord+dx*x+dy*y).rgb;bigAverage+=sample;bigTotal+=1.0;if(abs(x)+abs(y)<2.0){smallAverage+=sample;smallTotal+=1.0;}}}vec3 edge=max(vec3(0.0),bigAverage/bigTotal-smallAverage/smallTotal);gl_FragColor=vec4(color.rgb-dot(edge,edge)*strength*100000.0,color.a);}");
f.call(this,a.ink,{strength:b*b*b*b*b,texSize:[this.width,this.height]});return this}function Z(b,d,c,e){a.bulgePinch=a.bulgePinch||u("uniform float radius;uniform float strength;uniform vec2 center;","coord-=center;float distance=length(coord);if(distance<radius){float percent=distance/radius;if(strength>0.0){coord*=mix(1.0,smoothstep(0.0,radius/distance,percent),strength*0.75);}else{coord*=mix(1.0,pow(percent,1.0+strength*0.75)*radius/distance,1.0-percent);}}coord+=center;");
f.call(this,a.bulgePinch,{radius:c,strength:q(-1,e,1),center:[b,d],texSize:[this.width,this.height]});return this}function $(b,d,c){a.matrixWarp=a.matrixWarp||u("uniform mat3 matrix;uniform bool useTextureSpace;","if(useTextureSpace)coord=coord/texSize*2.0-1.0;vec3 warp=matrix*vec3(coord,1.0);coord=warp.xy/warp.z;if(useTextureSpace)coord=(coord*0.5+0.5)*texSize;");b=Array.prototype.concat.apply([],b);if(4==b.length)b=
[b[0],b[1],0,b[2],b[3],0,0,0,1];else if(9!=b.length)throw"can only warp with 2x2 or 3x3 matrix";f.call(this,a.matrixWarp,{matrix:d?y(b):b,texSize:[this.width,this.height],useTextureSpace:c|0});return this}function aa(a,d){var c=x.apply(null,d),e=x.apply(null,a),c=y(c);return this.matrixWarp([c[0]*e[0]+c[1]*e[3]+c[2]*e[6],c[0]*e[1]+c[1]*e[4]+c[2]*e[7],c[0]*e[2]+c[1]*e[5]+c[2]*e[8],c[3]*e[0]+c[4]*e[3]+c[5]*e[6],c[3]*e[1]+c[4]*e[4]+c[5]*e[7],c[3]*e[2]+c[4]*e[5]+c[5]*e[8],c[6]*e[0]+c[7]*e[3]+c[8]*e[6],
c[6]*e[1]+c[7]*e[4]+c[8]*e[7],c[6]*e[2]+c[7]*e[5]+c[8]*e[8]])}function ba(b,d,c,e){a.swirl=a.swirl||u("uniform float radius;uniform float angle;uniform vec2 center;","coord-=center;float distance=length(coord);if(distance<radius){float percent=(radius-distance)/radius;float theta=percent*percent*angle;float s=sin(theta);float c=cos(theta);coord=vec2(coord.x*c-coord.y*s,coord.x*s+coord.y*c);}coord+=center;");
f.call(this,a.swirl,{radius:c,center:[b,d],angle:e,texSize:[this.width,this.height]});return this}var v={};(function(){function a(b){if(!b.getExtension("OES_texture_float"))return!1;var c=b.createFramebuffer(),e=b.createTexture();b.bindTexture(b.TEXTURE_2D,e);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,b.NEAREST);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,b.NEAREST);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE);
b.texImage2D(b.TEXTURE_2D,0,b.RGBA,1,1,0,b.RGBA,b.UNSIGNED_BYTE,null);b.bindFramebuffer(b.FRAMEBUFFER,c);b.framebufferTexture2D(b.FRAMEBUFFER,b.COLOR_ATTACHMENT0,b.TEXTURE_2D,e,0);c=b.createTexture();b.bindTexture(b.TEXTURE_2D,c);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,b.LINEAR);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,b.LINEAR);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE);b.texImage2D(b.TEXTURE_2D,
0,b.RGBA,2,2,0,b.RGBA,b.FLOAT,new Float32Array([2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]));var e=b.createProgram(),d=b.createShader(b.VERTEX_SHADER),g=b.createShader(b.FRAGMENT_SHADER);b.shaderSource(d,"attribute vec2 vertex;void main(){gl_Position=vec4(vertex,0.0,1.0);}");b.shaderSource(g,"uniform sampler2D texture;void main(){gl_FragColor=texture2D(texture,vec2(0.5));}");b.compileShader(d);b.compileShader(g);b.attachShader(e,d);b.attachShader(e,
g);b.linkProgram(e);d=b.createBuffer();b.bindBuffer(b.ARRAY_BUFFER,d);b.bufferData(b.ARRAY_BUFFER,new Float32Array([0,0]),b.STREAM_DRAW);b.enableVertexAttribArray(0);b.vertexAttribPointer(0,2,b.FLOAT,!1,0,0);d=new Uint8Array(4);b.useProgram(e);b.viewport(0,0,1,1);b.bindTexture(b.TEXTURE_2D,c);b.drawArrays(b.POINTS,0,1);b.readPixels(0,0,1,1,b.RGBA,b.UNSIGNED_BYTE,d);return 127===d[0]||128===d[0]}function d(){}function c(a){"OES_texture_float_linear"===a?(void 0===this.$OES_texture_float_linear$&&Object.defineProperty(this,
"$OES_texture_float_linear$",{enumerable:!1,configurable:!1,writable:!1,value:new d}),a=this.$OES_texture_float_linear$):a=n.call(this,a);return a}function e(){var a=f.call(this);-1===a.indexOf("OES_texture_float_linear")&&a.push("OES_texture_float_linear");return a}try{var g=document.createElement("canvas").getContext("experimental-webgl")}catch(l){}if(g&&-1===g.getSupportedExtensions().indexOf("OES_texture_float_linear")&&a(g)){var n=WebGLRenderingContext.prototype.getExtension,f=WebGLRenderingContext.prototype.getSupportedExtensions;
WebGLRenderingContext.prototype.getExtension=c;WebGLRenderingContext.prototype.getSupportedExtensions=e}})();var a;v.canvas=function(){var b=document.createElement("canvas");try{a=b.getContext("experimental-webgl",{premultipliedAlpha:!1})}catch(d){a=null}if(!a)throw"This browser does not support WebGL";b._={gl:a,isInitialized:!1,texture:null,spareTexture:null,flippedShader:null};b.texture=k(A);b.draw=k(C);b.update=k(D);b.replace=k(E);b.contents=k(F);b.getPixelArray=k(G);b.brightnessContrast=k(H);
b.hexagonalPixelate=k(X);b.hueSaturation=k(K);b.colorHalftone=k(U);b.triangleBlur=k(S);b.unsharpMask=k(N);b.perspective=k(aa);b.matrixWarp=k($);b.bulgePinch=k(Z);b.tiltShift=k(R);b.dotScreen=k(V);b.edgeWork=k(W);b.lensBlur=k(Q);b.zoomBlur=k(T);b.noise=k(L);b.denoise=k(J);b.curves=k(I);b.swirl=k(ba);b.ink=k(Y);b.vignette=k(P);b.vibrance=k(O);b.sepia=k(M);return b};v.splineInterpolate=t;var h=function(){function b(b,c){var e=a.createShader(b);a.shaderSource(e,c);a.compileShader(e);if(!a.getShaderParameter(e,
a.COMPILE_STATUS))throw"compile error: "+a.getShaderInfoLog(e);return e}function d(d,l){this.texCoordAttribute=this.vertexAttribute=null;this.program=a.createProgram();d=d||c;l=l||e;l="precision highp float;"+l;a.attachShader(this.program,b(a.VERTEX_SHADER,d));a.attachShader(this.program,b(a.FRAGMENT_SHADER,l));a.linkProgram(this.program);if(!a.getProgramParameter(this.program,a.LINK_STATUS))throw"link error: "+a.getProgramInfoLog(this.program);}var c="attribute vec2 vertex;attribute vec2 _texCoord;varying vec2 texCoord;void main(){texCoord=_texCoord;gl_Position=vec4(vertex*2.0-1.0,0.0,1.0);}",
e="uniform sampler2D texture;varying vec2 texCoord;void main(){gl_FragColor=texture2D(texture,texCoord);}";d.prototype.destroy=function(){a.deleteProgram(this.program);this.program=null};d.prototype.uniforms=function(b){a.useProgram(this.program);for(var e in b)if(b.hasOwnProperty(e)){var c=a.getUniformLocation(this.program,e);if(null!==c){var d=b[e];if("[object Array]"==Object.prototype.toString.call(d))switch(d.length){case 1:a.uniform1fv(c,new Float32Array(d));break;
case 2:a.uniform2fv(c,new Float32Array(d));break;case 3:a.uniform3fv(c,new Float32Array(d));break;case 4:a.uniform4fv(c,new Float32Array(d));break;case 9:a.uniformMatrix3fv(c,!1,new Float32Array(d));break;case 16:a.uniformMatrix4fv(c,!1,new Float32Array(d));break;default:throw"dont't know how to load uniform \""+e+'" of length '+d.length;}else if("[object Number]"==Object.prototype.toString.call(d))a.uniform1f(c,d);else throw'attempted to set uniform "'+e+'" to invalid value '+(d||"undefined").toString();
}}return this};d.prototype.textures=function(b){a.useProgram(this.program);for(var c in b)b.hasOwnProperty(c)&&a.uniform1i(a.getUniformLocation(this.program,c),b[c]);return this};d.prototype.drawRect=function(b,c,e,d){var f=a.getParameter(a.VIEWPORT);c=void 0!==c?(c-f[1])/f[3]:0;b=void 0!==b?(b-f[0])/f[2]:0;e=void 0!==e?(e-f[0])/f[2]:1;d=void 0!==d?(d-f[1])/f[3]:1;null==a.vertexBuffer&&(a.vertexBuffer=a.createBuffer());a.bindBuffer(a.ARRAY_BUFFER,a.vertexBuffer);a.bufferData(a.ARRAY_BUFFER,new Float32Array([b,
c,b,d,e,c,e,d]),a.STATIC_DRAW);null==a.texCoordBuffer&&(a.texCoordBuffer=a.createBuffer(),a.bindBuffer(a.ARRAY_BUFFER,a.texCoordBuffer),a.bufferData(a.ARRAY_BUFFER,new Float32Array([0,0,0,1,1,0,1,1]),a.STATIC_DRAW));null==this.vertexAttribute&&(this.vertexAttribute=a.getAttribLocation(this.program,"vertex"),a.enableVertexAttribArray(this.vertexAttribute));null==this.texCoordAttribute&&(this.texCoordAttribute=a.getAttribLocation(this.program,"_texCoord"),a.enableVertexAttribArray(this.texCoordAttribute));
a.useProgram(this.program);a.bindBuffer(a.ARRAY_BUFFER,a.vertexBuffer);a.vertexAttribPointer(this.vertexAttribute,2,a.FLOAT,!1,0,0);a.bindBuffer(a.ARRAY_BUFFER,a.texCoordBuffer);a.vertexAttribPointer(this.texCoordAttribute,2,a.FLOAT,!1,0,0);a.drawArrays(a.TRIANGLE_STRIP,0,4)};d.getDefaultShader=function(){a.defaultShader=a.defaultShader||new d;return a.defaultShader};return d}();z.prototype.interpolate=function(a){for(var d=0,c=this.ya.length-1;1<c-d;){var e=c+d>>1;this.xa[e]>a?c=e:d=e}var e=this.xa[c]-
this.xa[d],g=(this.xa[c]-a)/e;a=(a-this.xa[d])/e;return g*this.ya[d]+a*this.ya[c]+((g*g*g-g)*this.y2[d]+(a*a*a-a)*this.y2[c])*e*e/6};var r=function(){function b(b,c,d,f){this.gl=a;this.id=a.createTexture();this.width=b;this.height=c;this.format=d;this.type=f;a.bindTexture(a.TEXTURE_2D,this.id);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.LINEAR);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE);a.texParameteri(a.TEXTURE_2D,
a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE);b&&c&&a.texImage2D(a.TEXTURE_2D,0,this.format,b,c,0,this.format,this.type,null)}function d(a){null==c&&(c=document.createElement("canvas"));c.width=a.width;c.height=a.height;a=c.getContext("2d");a.clearRect(0,0,c.width,c.height);return a}b.fromElement=function(c){var d=new b(0,0,a.RGBA,a.UNSIGNED_BYTE);d.loadContentsOf(c);return d};b.prototype.loadContentsOf=function(b){this.width=b.width||b.videoWidth;this.height=b.height||b.videoHeight;a.bindTexture(a.TEXTURE_2D,
this.id);a.texImage2D(a.TEXTURE_2D,0,this.format,this.format,this.type,b)};b.prototype.initFromBytes=function(b,c,d){this.width=b;this.height=c;this.format=a.RGBA;this.type=a.UNSIGNED_BYTE;a.bindTexture(a.TEXTURE_2D,this.id);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,b,c,0,a.RGBA,this.type,new Uint8Array(d))};b.prototype.destroy=function(){a.deleteTexture(this.id);this.id=null};b.prototype.use=function(b){a.activeTexture(a.TEXTURE0+(b||0));a.bindTexture(a.TEXTURE_2D,this.id)};b.prototype.unuse=function(b){a.activeTexture(a.TEXTURE0+
(b||0));a.bindTexture(a.TEXTURE_2D,null)};b.prototype.ensureFormat=function(b,c,d,f){if(1==arguments.length){var h=arguments[0];b=h.width;c=h.height;d=h.format;f=h.type}if(b!=this.width||c!=this.height||d!=this.format||f!=this.type)this.width=b,this.height=c,this.format=d,this.type=f,a.bindTexture(a.TEXTURE_2D,this.id),a.texImage2D(a.TEXTURE_2D,0,this.format,b,c,0,this.format,this.type,null)};b.prototype.drawTo=function(b){a.framebuffer=a.framebuffer||a.createFramebuffer();a.bindFramebuffer(a.FRAMEBUFFER,
a.framebuffer);a.framebufferTexture2D(a.FRAMEBUFFER,a.COLOR_ATTACHMENT0,a.TEXTURE_2D,this.id,0);if(a.checkFramebufferStatus(a.FRAMEBUFFER)!==a.FRAMEBUFFER_COMPLETE)throw Error("incomplete framebuffer");a.viewport(0,0,this.width,this.height);b();a.bindFramebuffer(a.FRAMEBUFFER,null)};var c=null;b.prototype.fillUsingCanvas=function(b){b(d(this));this.format=a.RGBA;this.type=a.UNSIGNED_BYTE;a.bindTexture(a.TEXTURE_2D,this.id);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,a.RGBA,a.UNSIGNED_BYTE,c);return this};
b.prototype.toImage=function(b){this.use();h.getDefaultShader().drawRect();var f=4*this.width*this.height,k=new Uint8Array(f),n=d(this),p=n.createImageData(this.width,this.height);a.readPixels(0,0,this.width,this.height,a.RGBA,a.UNSIGNED_BYTE,k);for(var m=0;m<f;m++)p.data[m]=k[m];n.putImageData(p,0,0);b.src=c.toDataURL()};b.prototype.swapWith=function(a){var b;b=a.id;a.id=this.id;this.id=b;b=a.width;a.width=this.width;this.width=b;b=a.height;a.height=this.height;this.height=b;b=a.format;a.format=
this.format;this.format=b};return b}(),s="float random(vec3 scale,float seed){return fract(sin(dot(gl_FragCoord.xyz+seed,scale))*43758.5453+seed);}";return v}();

354
filters/dog/main.js Normal file
View File

@ -0,0 +1,354 @@
"use strict";
// some globalz:
let THREECAMERA = null;
let ISDETECTED = false;
let TONGUEMESH = null, NOSEMESH = null, EARMESH = null;
let DOGOBJ3D = null, FRAMEOBJ3D = null;
let ISOVERTHRESHOLD = false, ISUNDERTRESHOLD = true;
let ISLOADED = false;
let MIXER = null;
let ACTION = null;
let ISANIMATING = false;
let ISOPAQUE = false;
let ISTONGUEOUT = false;
let ISANIMATIONOVER = false;
let _flexParts = [];
let _videoGeometry = null;
// callback: launched if a face is detected or lost
function detect_callback(isDetected) {
if (isDetected) {
console.log('INFO in detect_callback(): DETECTED');
} else {
console.log('INFO in detect_callback(): LOST');
}
}
function create_mat2d(threeTexture, isTransparent){ // MT216: we put the creation of the video material in a func because we will also use it for the frame
return new THREE.RawShaderMaterial({
depthWrite: false,
depthTest: false,
transparent: isTransparent,
vertexShader: "attribute vec2 position;\n\
varying vec2 vUV;\n\
void main(void){\n\
gl_Position = vec4(position, 0., 1.);\n\
vUV = 0.5 + 0.5 * position;\n\
}",
fragmentShader: "precision lowp float;\n\
uniform sampler2D samplerVideo;\n\
varying vec2 vUV;\n\
void main(void){\n\
gl_FragColor = texture2D(samplerVideo, vUV);\n\
}",
uniforms:{
samplerVideo: { value: threeTexture }
}
});
}
function applyFilter() {
let canvas;
try {
canvas = fx.canvas();
} catch (e) {
alert('Ow no! WebGL isn\'t supported...')
return
}
const tempImage = new Image(512, 512);
tempImage.src = './images/texture_pink.jpg';
tempImage.onload = () => {
const texture = canvas.texture(tempImage);
// Create the effet
canvas.draw(texture).vignette(0.5, 0.6).update();
const canvasOpacity = document.createElement('canvas');
canvasOpacity.width = 512;
canvasOpacity.height = 512;
const ctx = canvasOpacity.getContext('2d');
ctx.globalAlpha = 0.2
ctx.drawImage(canvas, 0, 0, 512, 512);
// Add the effect
const calqueMesh = new THREE.Mesh(_videoGeometry, create_mat2d(new THREE.TextureLoader().load(canvasOpacity.toDataURL('image/png')), true))
calqueMesh.material.opacity = 0.2;
calqueMesh.material.transparent = true;
calqueMesh.renderOrder = 999; // render last
calqueMesh.frustumCulled = false;
FRAMEOBJ3D.add(calqueMesh);
}
}
// build the 3D. called once when Jeeliz Face Filter is OK
function init_threeScene(spec) {
// INIT THE THREE.JS context
const threeStuffs = JeelizThreeHelper.init(spec, detect_callback);
_videoGeometry = threeStuffs.videoMesh.geometry;
// CREATE OUR DOG EARS:
// let's begin by creating a loading manager that will allow us to
// have more control over the three parts of our dog model
const loadingManager = new THREE.LoadingManager();
const loaderEars = new THREE.BufferGeometryLoader(loadingManager);
loaderEars.load(
'./models/dog/dog_ears.json',
function (geometry) {
const mat = new THREE.FlexMaterial({
map: new THREE.TextureLoader().load('./models/dog/texture_ears.jpg'),
flexMap: new THREE.TextureLoader().load('./models/dog/flex_ears_256.jpg'),
alphaMap: new THREE.TextureLoader().load('./models/dog/alpha_ears_256.jpg'),
transparent: true,
opacity: 1,
bumpMap: new THREE.TextureLoader().load('./models/dog/normal_ears.jpg'),
bumpScale: 0.0075,
shininess: 1.5,
specular: 0xffffff,
});
EARMESH = new THREE.Mesh(geometry, mat);
EARMESH.scale.multiplyScalar(0.025);
EARMESH.position.setY(-0.3);
EARMESH.frustumCulled = false;
EARMESH.renderOrder = 10000;
EARMESH.material.opacity.value = 1;
}
);
// CREATE OUR DOG NOSE
const loaderNose = new THREE.BufferGeometryLoader(loadingManager);
loaderNose.load(
'./models/dog/dog_nose.json',
function (geometry) {
const mat = new THREE.MeshPhongMaterial({
map: new THREE.TextureLoader().load('./models/dog/texture_nose.jpg'),
shininess: 1.5,
specular: 0xffffff,
bumpMap: new THREE.TextureLoader().load('./models/dog/normal_nose.jpg'),
bumpScale: 0.005
});
NOSEMESH = new THREE.Mesh(geometry, mat);
NOSEMESH.scale.multiplyScalar(0.018);
NOSEMESH.position.setY(-0.05);
NOSEMESH.position.setZ(0.15);
NOSEMESH.frustumCulled = false;
NOSEMESH.renderOrder = 10000;
}
);
// CREATE OUR DOG TONGUE
const loaderTongue = new THREE.JSONLoader(loadingManager);
loaderTongue.load(
'models/dog/dog_tongue.json',
function (geometry) {
geometry.computeMorphNormals();
const mat = new THREE.FlexMaterial({
map: new THREE.TextureLoader().load('./models/dog/dog_tongue.jpg'),
flexMap: new THREE.TextureLoader().load('./models/dog/flex_tongue_256.png'),
alphaMap: new THREE.TextureLoader().load('./models/dog/tongue_alpha_256.jpg'),
transparent: true,
morphTargets: true,
opacity: 1
});
TONGUEMESH = new THREE.Mesh(geometry, mat);
TONGUEMESH.material.opacity.value = 0;
TONGUEMESH.scale.multiplyScalar(2);
TONGUEMESH.position.setY(-0.28);
TONGUEMESH.frustumCulled = false;
TONGUEMESH.visible = false;
if (!MIXER) {
// the mixer is declared globally so we can use it in the THREE renderer
MIXER = new THREE.AnimationMixer(TONGUEMESH);
const clips = TONGUEMESH.geometry.animations;
const clip = clips[0];
ACTION = MIXER.clipAction(clip);
ACTION.noLoop = true;
ACTION.play();
}
}
);
loadingManager.onLoad = () => {
DOGOBJ3D.add(EARMESH);
DOGOBJ3D.add(NOSEMESH);
DOGOBJ3D.add(TONGUEMESH);
addDragEventListener(DOGOBJ3D);
threeStuffs.faceObject.add(DOGOBJ3D);
ISLOADED = true;
}
// CREATE AN AMBIENT LIGHT
const ambient = new THREE.AmbientLight(0xffffff, 0.8);
threeStuffs.scene.add(ambient);
// CREAT A DIRECTIONALLIGHT
const dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
dirLight.position.set(100, 1000, 1000);
threeStuffs.scene.add(dirLight);
// CREATE THE CAMERA
THREECAMERA = JeelizThreeHelper.create_camera();
threeStuffs.scene.add(FRAMEOBJ3D);
// Add filter
applyFilter();
} // end init_threeScene()
function animateTongue (mesh, isReverse) {
mesh.visible = true;
if (isReverse) {
ACTION.timescale = -1;
ACTION.paused = false;
setTimeout(() => {
ACTION.paused = true;
ISOPAQUE = false;
ISTONGUEOUT = false;
ISANIMATING = false;
ISANIMATIONOVER = true;
new TWEEN.Tween(mesh.material.opacity)
.to({ value: 0 }, 150)
.start();
}, 150);
} else {
ACTION.timescale = 1;
ACTION.reset();
ACTION.paused = false;
new TWEEN.Tween(mesh.material.opacity)
.to({ value: 1 }, 100)
.onComplete(() => {
ISOPAQUE = true;
setTimeout(() => {
ACTION.paused = true;
ISANIMATING = false;
ISTONGUEOUT = true;
ISANIMATIONOVER = true;
}, 150);
})
.start();
}
}
// Entry point: launched by body.onload()
function main(){
DOGOBJ3D = new THREE.Object3D();
FRAMEOBJ3D = new THREE.Object3D();
JeelizResizer.size_canvas({
canvasId: 'jeeFaceFilterCanvas',
callback: function(isError, bestVideoSettings){
init_faceFilter(bestVideoSettings);
}
});
}
function init_faceFilter(videoSettings){
JEELIZFACEFILTER.init({
canvasId: 'jeeFaceFilterCanvas',
NNCPath: '../../../neuralNets/', // root of NN_DEFAULT.json file
videoSettings: videoSettings,
callbackReady: function (errCode, spec) {
if (errCode) {
console.log('AN ERROR HAPPENS. SORRY BRO :( . ERR =', errCode);
return;
}
console.log('INFO: JEELIZFACEFILTER IS READY');
init_threeScene(spec);
}, // end callbackReady()
// called at each render iteration (drawing loop)
callbackTrack: function (detectState) {
ISDETECTED = JeelizThreeHelper.get_isDetected();
if (ISDETECTED) {
const _quat = new THREE.Quaternion();
const _eul = new THREE.Euler();
_eul.setFromQuaternion(_quat);
// flex ears material:
if (EARMESH && EARMESH.material.set_amortized){
EARMESH.material.set_amortized(
EARMESH.getWorldPosition(new THREE.Vector3(0,0,0)),
EARMESH.getWorldScale(new THREE.Vector3(0,0,0)),
EARMESH.getWorldQuaternion(_eul),
false,
0.1
);
}
if (TONGUEMESH && TONGUEMESH.material.set_amortized){
TONGUEMESH.material.set_amortized(
TONGUEMESH.getWorldPosition(new THREE.Vector3(0,0,0)),
TONGUEMESH.getWorldScale(new THREE.Vector3(0,0,0)),
TONGUEMESH.getWorldQuaternion(_eul),
false,
0.3
);
}
if (detectState.expressions[0] >= 0.85 && !ISOVERTHRESHOLD) {
ISOVERTHRESHOLD = true;
ISUNDERTRESHOLD = false;
ISANIMATIONOVER = false;
}
if (detectState.expressions[0] <= 0.1 && !ISUNDERTRESHOLD) {
ISOVERTHRESHOLD = false;
ISUNDERTRESHOLD = true;
ISANIMATIONOVER = false;
}
if (ISLOADED && ISOVERTHRESHOLD && !ISANIMATING && !ISANIMATIONOVER) {
if (!ISTONGUEOUT) {
ISANIMATING = true;
animateTongue(TONGUEMESH);
} else {
ISANIMATING = true;
animateTongue(TONGUEMESH, true);
}
}
}
TWEEN.update();
// Update the mixer on each frame:
if (ISOPAQUE) {
MIXER.update(0.16);
}
JeelizThreeHelper.render(detectState, THREECAMERA);
} // end callbackTrack()
}); // end JEELIZFACEFILTER.init call
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

3
filters/readme.md Normal file
View File

@ -0,0 +1,3 @@
Some of the code in this folder is based on the sample code made available from Jeeliz;
Apache-2.0 License
https://github.com/jeeliz/jeelizFaceFilter/blob/master/LICENSE

65
filters/sample.js Normal file
View File

@ -0,0 +1,65 @@
function effectsEngine(){
console.log('LOADED SAMPLE');
function loadScript(url, callback=false){
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = function(){this.remove();if(callback){callback();}}
document.head.appendChild(script);
}
loadScript("./thirdparty/jeeliz/jeelizFaceFilter.js",
function(){
loadScript("./thirdparty/jeeliz/helpers/JeelizCanvas2DHelper.js");
}
);
function main(){ // entry point
console.log(".");
if (session.canvasSource && session.canvasSource.videoWidth && session.canvasSource.videoHeight && session.canvasWebGL){
if (session.canvasWebGL && !(document.getElementById("effectsCanvasTarget"))){
session.canvasWebGL.id = "effectsCanvasTarget";
session.canvasWebGL.style.position="fixed";
session.canvasWebGL.style.top= "-9999px";
session.canvasWebGL.style.left= "-9999px";
document.body.appendChild(session.canvasWebGL);
}
start(JEELIZFACEFILTER, "effectsCanvasTarget", session.canvasSource, 'yellow');
} else {
setTimeout(main, 500);
}
}
function start(jeeFaceFilterAPIInstance, canvasId, videoElement, borderColor){
let cvd = null; // return of Canvas2DDisplay
jeeFaceFilterAPIInstance.init({
canvasId: canvasId,
videoSettings: {
videoElement: videoElement
},
NNCPath: './thirdparty/jeeliz/neuralNets/', // root of NN_DEFAULT.json file
callbackReady: function(errCode, spec){
if (errCode){
console.log('AN ERROR HAPPENS. SORRY BRO :( . ERR =', errCode);
return;
}
console.log('INFO: JEELIZFACEFILTER IS READY');
cvd = JeelizCanvas2DHelper(spec);
cvd.ctx.strokeStyle = borderColor;
},
callbackTrack: function(detectState){ // drawing loop
if (detectState.detected>0.6){
// draw a border around the face:
const faceCoo = cvd.getCoordinates(detectState);
cvd.ctx.clearRect(0,0,cvd.canvas.width, cvd.canvas.height);
cvd.ctx.strokeRect(faceCoo.x, faceCoo.y, faceCoo.w, faceCoo.h);
cvd.update_canvasTexture();
}
cvd.draw();
}
});
}
main();
};

425
fr.json Normal file
View File

@ -0,0 +1,425 @@
{
"titles": {
"toggle-the-chat": "Réduire le tchat",
"mute-the-speaker": "Mettre le haut parleur en sourdine",
"mute-the-mic": "Mettre le micro en sourdine",
"disable-the-camera": "Désactiver la caméra",
"settings": "Paramètres",
"hangup-the-call": "Raccrocher",
"show-help-info": "Montrer l'aide",
"language-options": "Options de langue",
"tip-hold-ctrl-command-to-select-multiple": "conseil : Maintenir CTRL (ou command) pour sélectionner plusieurs",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Idéal pour le format gaming 1080p60 si votre ordinateur le permet",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Meilleure compression et qualité vidéo en contrepartie d'un chargement d'encodage CPU plus long",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Désactiver les effets audio et augmenter le débit audio",
"the-guest-will-not-have-a-choice-over-audio-options": "L'invité ne pourra pas modifier les paramètres audio",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "L'invité pourra uniquement modifier sa caméra",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Maintenir CTRL (ou command) et rouler la molette de la souris pour effectuer un zoom avant et arrière à distance sur les flux vidéo compatibles",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Ajouter un mot de passe pour que le flux vidéo ne soit pas accessible au personne n'ayant pas le mot de passe",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Ajouter l'invité à une salle de discussion en groupe; elle sera créée automatiquement si nécessaire.",
"customize-the-room-settings-for-this-guest": "Personnaliser les paramètres de la salle pour cet invité",
"hold-ctrl-or-cmd-to-select-multiple-files": "Maintenir CTRL (ou command) pour sélectionner plusieurs fichiers à la fois",
"enter-an-https-url": "Saisir une URL en HTTPS",
"lucy-g": "Lucy G",
"flaticon": "Flaticon",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"gregor-cresnar": "Gregor Cresnar",
"add-this-video-to-any-remote-scene-1-": "Ajouter cette vidéo à n'importe quel flux '&scene=1'",
"forward-user-to-another-room-they-can-always-return-": "Transférer l'utilisateur vers une autre salle. Il pourra toujours revenir par la suite.",
"start-recording-this-stream-experimental-views": "Commencer à enregistrer ce flux. *experimental*' views",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Forcer la déconnexion de l'utilisateur. Il pourra toujours se reconnecter par la suite.",
"change-this-audio-s-volume-in-all-remote-scene-views": "Modifier le volume de l'audio sur l'ensemble des flux '&scene' views",
"remotely-mute-this-audio-in-all-remote-scene-views": "Mettre en sourdine l'audio sur l'ensemble des flux '&scene' views",
"disable-video-preview": "Désactiver l'aperçu vidéo",
"low-quality-preview": "Aperçu faible définition",
"high-quality-preview": "Aperçu haute définition",
"send-direct-message": "Envoyer un message direct",
"advanced-settings-and-remote-control": "Paramètres avancés et contrôle à distance",
"toggle-voice-chat-with-this-guest": "Basculer le tchat vocal pour cet utilisateur",
"join-by-room-name-here": "Enter a room name to quick join",
"join-room": "Join room",
"share-a-screen-with-others": "Share a Screen with others",
"alert-the-host-you-want-to-speak": "Alert the host you want to speak",
"record-your-stream-to-disk": "Record your stream to disk",
"cancel-the-director-s-video-audio": "Cancel the Director's Video/Audio",
"submit-any-error-logs": "Submit any error logs",
"add-group-chat-to-obs": "Add Group Chat to OBS",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "For large group rooms, this option can reduce the load on remote guests substantially",
"which-video-codec-would-you-want-used-by-default-": "Which video codec would you want used by default?",
"you-ll-enter-as-the-room-s-director": "You'll enter as the room's director",
"add-your-camera-to-obs": "Add your Camera to OBS",
"remote-screenshare-into-obs": "Remote Screenshare into OBS",
"create-reusable-invite": "Create Reusable Invite",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "Encode the URL so that it's harder for a guest to modify the settings.",
"more-options": "More Options",
"youtube-video-demoing-how-to-do-this": "Youtube Video demoing how to do this",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Invite a guest or camera source to publish into the group room",
"if-enabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If enabled, the invited guest will not be able to see or hear anyone in the room.",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Use this link in the OBS Browser Source to capture the video or audio",
"if-enabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If enabled, you must manually add a video to a scene for it to appear.",
"disables-echo-cancellation-and-improves-audio-quality": "Disables Echo Cancellation and improves audio quality",
"audio-only-sources-are-visually-hidden-from-scenes": "Audio-only sources are visually hidden from scenes",
"guest-will-be-prompted-to-enter-a-display-name": "Guest will be prompted to enter a Display Name",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Display Names will be shown in the bottom-left corner of videos",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "Request 1080p60 from the Guest instead of 720p60, if possible",
"the-default-microphone-will-be-pre-selected-for-the-guest": "The default microphone will be pre-selected for the guest",
"the-default-camera-device-will-selected-automatically": "The default camera device will selected automatically",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "The guest won't have access to changing camera settings or screenshare",
"the-guest-will-not-see-their-own-self-preview-after-joining": "The guest will not see their own self-preview after joining",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Guests will have an option to poke the Director by pressing a button",
"add-an-audio-compressor-to-the-guest-s-microphone": "Add an audio compressor to the guest's microphone",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Add an Equalizer to the guest's microphone that the director can control",
"the-guest-can-only-see-the-director-s-video-if-provided": "The guest can only see the Director's video, if provided",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "The guest's microphone will be muted on joining. They can unmute themselves.",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Have the guest join muted, so only the director can Unmute the guest.",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Make the invite URL encoded, so parameters are harder to tinker with by guests",
"move-the-user-to-another-room-controlled-by-another-director": "Move the user to another room, controlled by another director",
"send-a-direct-message-to-this-user-": "Send a Direct Message to this user.",
"remotely-change-the-volume-of-this-guest": "Remotely change the volume of this guest",
"mute-this-guest-everywhere": "Mute this guest everywhere",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Start Recording this remote stream to this local drive. *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "The Remote Guest will record their local stream to their local drive. *experimental*",
"shift-this-video-down-in-order": "Shift this Video Down in Order",
"current-index-order-of-this-video": "Current Index Order of this Video",
"shift-this-video-up-in-order": "Shift this Video Up in Order",
"remote-audio-settings": "Remote Audio Settings",
"advanced-video-settings": "Advanced Video Settings",
"activate-or-reload-this-video-device-": "Activate or Reload this video device.",
"load-the-next-guest-in-queue": "Load the next guest in queue",
"create-a-secondary-stream": "Create a Secondary Stream",
"add-to-calendar": "Add to Calendar",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "The director will be visible in scenes, as if a performer themselves.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Useful if you want to perform and direct at the same time",
"start-streaming": "start streaming",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If disabled, the invited guest will not be able to see or hear anyone in the room.",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If disabled, you must manually add a video to a scene for it to appear.",
"guests-not-actively-speaking-will-be-hidden": "Guests not actively speaking will be hidden",
"increase-video-quality-that-guests-in-room-see-": "Increase video quality that guests in room see.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "The guest will not be asked for a video device on connection",
"toggle-solo-voice-chat": "Toggle Solo Voice Chat",
"add-to-scene-2": "Add to Scene 2",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"set-to-default-audio-channel": "Set to Default Audio Channel",
"set-to-audio-channel-1": "Set to Audio Channel 1",
"set-to-audio-channel-2": "Set to Audio Channel 2",
"set-to-audio-channel-3": "Set to Audio Channel 3",
"set-to-audio-channel-4": "Set to Audio Channel 4",
"set-to-audio-channel-5": "Set to Audio Channel 5",
"toggle-the-remote-guest-s-speaker-output": "Toggle the remote guest's speaker output",
"toggle-the-remote-guest-s-display-output": "Toggle the remote guest's display output",
"set-to-audio-channel-6": "Set to Audio Channel 6",
"set-to-audio-channel-7": "Set to Audio Channel 7",
"set-to-audio-channel-8": "Set to Audio Channel 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"solo-this-video-everywhere": "Solo this video everywhere",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"cannot-see-videos": "Cannot see videos",
"cannot-hear-others": "Cannot hear others",
"see-director-only": "See director only",
"show-mini-preview": "Show Mini preview",
"raise-hand-button": "Raise hand button",
"show-labels": "Show labels",
"transfer-to-a-new-room": "Transfer to a new Room",
"enable-custom-password": "Enable custom password",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"add-this-video-to-any-remote-scene-2-": "Add this Video to any remote '&scene=2'",
"add-to-scene-8": "Add to Scene 8",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees",
"enter-a-room-name-to-quick-join": "Enter a room name to quick join"
},
"innerHTML": {
"logo-header": "<font id=\"qos\" style=\"color: white;\">O</font>BS.Ninja ",
"copy-this-url": "Copiez cette URL dans un OBS \"Browser Source\"",
"you-are-in-the-control-center": "Vous êtes dans le centre de contrôle de la pièce",
"joining-room": "Vous rejoignez la salle",
"add-group-chat": "Conversation de groupe",
"rooms-allow-for": "Les salles permettent une conversation de groupe simplifiée et la gestion avancée de plusieurs flux à la fois.",
"room-name": "Nom de la salle",
"password-input-field": "Mot de passe",
"enter-the-rooms-control": "Entrez dans le centre de contrôle de la pièce",
"show-tips": "Montrez-moi quelques conseils ...",
"added-notes": "\n\t\t\t\t<u><i>Added Notes:</i></u>\n\t\t\t\t<li>N'importe qui peut entrer dans une pièce s'il en connaît le nom, alors gardez-le unique</li>\n\t\t\t\t<li>Il n'est pas conseillé d'avoir plus de quatre (4) personnes dans une pièce pour des raisons de performances, mais cela dépend de votre matériel.</li>\n\t\t\t\t<li>Les appareils iOS sont limités à des groupes de deux (2) personnes maximum. Il s'agit d'une limitation matérielle.</li>\n\t\t\t\t\n\t\t\t\t",
"back": "Retour",
"add-your-camera": "Ajoutez votre caméra à OBS",
"ask-for-permissions": "Autoriser l'accès à la caméra / au micro",
"waiting-for-camera": "Chargement de la caméra",
"video-source": "Source vidéo",
"max-resolution": "Résolution max.",
"balanced": "Équilibré",
"smooth-cool": "Lisse et frais",
"select-audio-source": "Sélectionner la source audio : \n\t\t\t\t\t",
"no-audio": "Pas de son",
"select-output-source": "Sélectionner la sortie audio ",
"remote-screenshare-obs": "Partage d'écran à distance dans OBS",
"note-share-audio": "\n\t\t\t\t\t<b>note</b>: N'oubliez pas de cliquer sur Partager l'audio dans Chrome.<br>(Firefox ne prend pas en charge le partage audio.)",
"select-screen-to-share": "Choisir l'écran à partager",
"audio-sources": "Sources audio",
"create-reusable-invite": "Créer une invitation réutilisable",
"here-you-can-pre-generate": "Ici, vous pouvez pré-générer un lien de source de navigateur réutilisable et un lien d'invitation d'invité associé.",
"generate-invite-link": "GÉNÉRER LE LIEN D'INVITATION",
"advanced-paramaters": "Paramètres avancés",
"unlock-video-bitrate": "Déverrouiller le débit vidéo (20 Mbps)",
"force-vp9-video-codec": "Forcer le codec vidéo VP9",
"enable-stereo-and-pro": "Activer l'audio stéréo et Pro HD",
"video-resolution": "Video Resolution: ",
"hide-mic-selection": "Forcer l'utilisation du micro par défaut",
"hide-screen-share": "Masquer les options de partage d'écran",
"allow-remote-control": "Contrôle à distance du zoom de la caméra (android)",
"add-a-password-to-stream": " Ajouter un mot de passe:",
"add-the-guest-to-a-room": " Ajouter l'invité à une salle:",
"invite-group-chat-type": "Cet invité peut:",
"can-see-and-hear": "Peut voir et entendre le tchat de groupe",
"can-hear-only": "Peut seulement entendre le tchat de groupe",
"cant-see-or-hear": "Ne peut ni entendre ni voir le tchat de groupe",
"share-local-video-file": "Partager un fichier média",
"share-website-iframe": "Partager site internet",
"run-a-speed-test": "Exécuter un test de vitesse",
"read-the-guides": "Parcourir les guides",
"info-blob": "\n\t\t\t\t\t\t<h2>Qu'est-ce que OBS.Ninja</h2><br>\n\t\t\t\t\t\t<li>100% <b> gratuit </b>; aucun téléchargement; aucune collecte de données personnelles; pas de connexion</li>\n\t\t\t\t\t\t<li>Importez des vidéos de votre smartphone, ordinateur portable, ordinateur ou de vos amis directement dans votre flux vidéo OBS</li>\n\t\t\t\t\t\t<li>Nous utilisons une technologie de transfert Peer-to-Peer de pointe qui offre une confidentialité et une latence ultra-faible</li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\t<li>Youtube video <i class=\"fa fa-youtube-play\" aria-hidden=\"true\"></i> <a href=\"https://www.youtube.com/watch?v=6R_sQKxFAhg\">Démonstration ici</a> </li>\n\t\t\t\t\t\t<h3>\n\t\t\t\t\t<i>Découvrez le <a href=\"https://www.reddit.com/r/OBSNinja/\">sub-reddit</a> <i class=\"fa fa-reddit-alien\" aria-hidden=\"true\"></i> pour de l'aide et des informations avancées.</i></h3>",
"add-to-scene": "Ajouter à la scène",
"forward-to-room": "Transférer",
"record": "Enregistrer",
"disconnect-guest": "Raccrocher",
"mute": "Mettre en sourdine",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Message",
"advanced-camera-settings": "<i class=\"las la-cog\"></i> Advanced",
"voice-chat": "<i class=\"las la-microphone\"></i> Voice Chat",
"open-in-new-tab": "Ouvrir dans un nouvel onglet",
"copy-to-clipboard": "Copier dans le presse-papier",
"click-for-quick-room-overview": "❔ Cliquez ici pour un aperçu rapide et de l'aide",
"push-to-talk-enable": "🔊 Permettre à l'administrateur d'utiliser le push-to-talk",
"welcome-to-control-room": "Welcome. This is the control-room for the group-chat. There are different things you can use this room for:<br><br>\t<li>You can host a group chat with friends using a room. Share the blue link to invite guests who will join the chat automatically.</li>\t<li>A group room can handle around 4 to 30 guests, depending on numerous factors, including CPU and available bandwidth of all guests in the room.</li>\t<li>Solo-views of each video are offered under videos as they load. These can be used within an OBS Browser Source.</li>\t<li>You can use the auto-mixing Group Scene, the green link, to auto arrange multiple videos for you in OBS.</li>\t<li>You can use this control room to record isolated video or audio streams, but it is an experimental feature still.</li>\t<li>Videos in the Director's room will be of low quality on purpose; to save bandwidth/CPU</li>\t<li>Guest's in the room will see each other's videos at a very limited quality to conserve bandwidth/CPU.</li>\t<li>OBS will see a guest's video in high-quality; the default video bitrate is 2500kbps.</li>\t<br>\tAs guests join, their videos will appear below. You can bring their video streams into OBS as solo-scenes or you can add them to the Group Scene.\t<br>The Group Scene auto-mixes videos that have been added to the group scene. Please note that the Auto-Mixer requires guests be manually added to it for them to appear in it; they are not added automatically.<br><br>Apple mobile devices, such as iPhones and iPads, do not fully support Video Group Chat. This is a hardware constraint.<br><br>\tFor advanced options and parameters, <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">see the Wiki.</a>",
"more-than-four-can-join": "These four guest slots are just for demonstration. More than four guests can actually join a room.",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tWelcome to OBS.Ninja! You can send text messages directly to connected peers from here.\n\t\t\t\t",
"names-and-labels-coming-soon": "\n\t\t\t\t\tNames identifying connected peers will be a feature in an upcoming release.\n\t\t\t\t",
"send-chat": "Send",
"available-languages": "Options de langues disponibles :",
"add-more-here": "Ajouter ici!",
"waiting-for-camera-to-load": "Chargement de votre caméra",
"start": "REJOINDRE",
"share-your-mic": "Partager votre micro",
"share-your-camera": "Partager votre caméra",
"share-your-screen": "Partager votre écran",
"join-room-with-mic": "Rejoindre la salle avec votre micro",
"share-screen-with-room": "Partager votre écran avec la salle",
"join-room-with-camera": "Rejoindre la salle avec votre caméra",
"click-start-to-join": "Appuyer sur rejoindre pour commencer",
"guests-only-see-director": "Guests can only see the Director's Video",
"default-codec-select": "Preferred Video Codec: ",
"obfuscate_url": "Obfuscate the Invite URL",
"hide-the-links": " LINKS (GUEST INVITES &amp; SCENES)",
"invite-users-to-join": "Guests can use the link to join the group room",
"this-is-obs-browser-source-link": "Use in OBS or other studio software to capture the group video mix",
"mute-scene": "mute in scene",
"mute-guest": "mute guest",
"record-local": " Record Local",
"record-remote": " Record Remote",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Audio Settings",
"scenes-can-see-director": "Director will also be a performer",
"select-digital-effect": " Digital Video Effects: ",
"add-a-password": " Add a Password:",
"hide-guest": "hide guest",
"toggle-remote-speaker": "Deafen Guest",
"toggle-remote-display": "Blind Guest",
"force-keyframe": "Rainbow Puke",
"change-url": "Change URL",
"change-params": "URL Params",
"solo-video": "Highlight guest",
"stats-remote": " Scene Stats",
"apply-new-guest-settings": "Apply settings",
"cancel": "Cancel",
"add-to-calendar": "Add details to your Calendar:",
"no-effects-applied": "No effects applied",
"blurred-background": "Blurred background",
"digital-greenscreen": "Digital greenscreen",
"virtual-background": "Virtual background",
"use-chrome-instead": "Consider using a Chromium-based browser instead.<br>\n \t\t\t\t\t\tSafari is more prone to having audio issues",
"select-the-video-files-to-share": "SELECT THE VIDEO FILES TO SHARE",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"click-here-for-help": "Click Here for a quick overview and help",
"guests-hear-others": "Guests hear others",
"capture-a-group-scene": "CAPTURE A GROUP SCENE",
"auto-add-guests": "Auto-add guests",
"pro-audio-mode": "Pro-audio mode",
"hide-audio-only-sources": "Hide audio-only sources",
"ask-for-display-name": "Ask for display name",
"show-display-names": "Show display names",
"show-active-speaker": "Show active speakers",
"auto-select-microphone": "Auto-select default microphone",
"auto-select-camera": "Auto-select default camera",
"hide-setting-buttons": "Hide settings button",
"mini-self-preview": "Mini self-preview",
"virtual-backgrounds": "Virtual backgrounds",
"powerful-computers-only": "Only use with powerful computers and small groups!!",
"guests-see-HD-video": "Guests see HD video",
"no-self-preview": "Disable self-preview",
"raise-hand-button": "Display 'raise-hand' button",
"enable-compressor": "Enable audio compressor",
"enable-equalizer": "Enable equalizer as option",
"low-cpu=broadcast-codec": "Low-CPU broadcast codec",
"only-see-director-feed": "Only see the director's feed",
"mute-microphone-by-default": "Mute microphone by default",
"guest-joins-with-no-camera": "Guest joins with no camera",
"unmute-by-director-only": "Unmute by director only",
"obfuscate-link": "Obfuscate link and parameters",
"this-can-reduce-packet-loss": "This can reduce video corruption caused by packet loss",
"use-h264-codec": "Use H264 codec",
"show-active-speakers": "Show active speakers",
"force-mono-audio": "Force mono audio",
"learn-more-about-params": "Learn more about URL parameters at ",
"More-scene-options": "More scene options",
"additional-controls": "Additional controls",
"select-local-image": "Select Local Image",
"close-settings": "Close Settings",
"advanced": "Advanced ",
"invisible-guests": "Not Visible",
"add-to-google-calendar": "Add to Google Calendar",
"add-to-outlook-calendar": "Add to Outlook Calendar",
"add-to-yahoo-calendar": "Add to Yahoo Calendar",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"fade-videos-in": "Fade videos in",
"show-guest-tips": "Show guest setup tips",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"add-to-scene2": "add to scene 2",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast..",
"join-room": "Join room",
"waiting-for-mic-to-load": "Waiting for mic to load"
},
"placeholders": {
"join-by-room-name-here": "Rejoindre via le nom de salle ici",
"enter-a-room-name-here": "Saisir un nom de salle ici",
"optional-room-password-here": "Mot de passe (optionnel) pour la salle ici",
"give-this-media-source-a-name-optional-": "Donner un nom à cette source média (optionnel)",
"add-an-optional-password": "Ajouter un mot de passe optionnel",
"enter-room-name-here": "Saisir le nom de la salle ici",
"enter-chat-message-to-send-here": "Saisir un message ici pour l'envoyer dans le tchat",
"optional": "optional",
"enter-the-room-name-here": "Enter the room name here",
"enter-the-room-password-here": "Enter the room password here",
"enter-your-message-here": "Enter your message here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "Enter a new Display Name for this stream",
"submit-error-report": "Press OK to submit any error logs to VDO.Ninja. Error logs may contain private information.",
"director-redirect-1": "The director wishes to redirect you to the URL: ",
"director-redirect-2": "\n\nPress OK to be redirected.",
"add-a-label": "Add a label",
"audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. You can try claiming the room after the first director leaves.</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.",
"director": "Director",
"unknown-user": "Unknown User",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?",
"load-previous-session": "Would you like to load your previous session's settings?",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.",
"enter-display-name": "Please enter your display name:",
"enter-new-display-name": "Enter a new Display Name for this stream",
"what-bitrate": "What bitrate would you like to record at? (kbps)",
"enter-website": "Enter a website URL to share",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.",
"share-with-trusted": "Share only with those you trust",
"pass-recommended": "A password is recommended",
"insecure-room-name": "Insecure room name.",
"allowed-chars": "Allowed chars",
"transfer": "transfer",
"armed": "armed",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)",
"change-url": "change URL",
"mute-in-scene": "mute in scene",
"unmute-guest": "un-mute guest",
"undeafen": "un-deafen",
"deafen": "deafen guest",
"unblind": "un-blind",
"blind": "blind guest",
"unmute": "un-mute",
"mute-guest": "mute guest",
"unhide": "unhide guest",
"hide-guest": "hide guest",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

View File

@ -12,8 +12,8 @@ iframe {
padding:0;
display:block;
margin:10px;
width:640px;
height:320px;
width:100%;
height:520px;
}
#viewlink {
width:400px;
@ -38,9 +38,7 @@ function loadIframe(){ // this is pretty important if you want to avoid camera
var iframe = document.createElement("iframe");
var iframeContainer = document.createElement("div");
var iframesrc = document.getElementById("viewlink").value;
iframe.allow="autoplay;camera;microphone";
iframe.allowtransparency="true";
iframe.allowfullscreen ="true";
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
if (iframesrc==""){
iframesrc="./";
@ -79,299 +77,523 @@ function loadIframe(){ // this is pretty important if you want to avoid camera
document.getElementById("container").appendChild(iframeContainer);
var button = document.createElement("button");
button.innerHTML = "Mute Speaker";
button.onclick = function(){iframe.contentWindow.postMessage({"mute":true}, '*');}; // "speaker" also works in the same way.
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Un-Mute Speaker";
button.onclick = function(){iframe.contentWindow.postMessage({"mute":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Toggle Speaker";
button.onclick = function(){iframe.contentWindow.postMessage({"mute":"toggle"}, '*');}; // open to a better suggestion here.
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Mute Mic";
button.onclick = function(){iframe.contentWindow.postMessage({"mic":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Un-Mute Mic";
button.onclick = function(){iframe.contentWindow.postMessage({"mic":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Toggle Mic";
button.onclick = function(){iframe.contentWindow.postMessage({"mic":"toggle"}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Mute Camera";
button.onclick = function(){iframe.contentWindow.postMessage({"camera":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Unmute Camera";
button.onclick = function(){iframe.contentWindow.postMessage({"camera":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Toggle Camera";
button.onclick = function(){iframe.contentWindow.postMessage({"camera":"toggle"}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Disconnect";
button.onclick = function(){iframe.contentWindow.postMessage({"close":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Low Bitrate";
button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":30}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "High Bitrate";
button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":5000}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Default Bitrate";
button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":-1}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Reload";
button.onclick = function(){iframe.contentWindow.postMessage({"reload":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "50% Volume";
button.onclick = function(){iframe.contentWindow.postMessage({"volume":0.5}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "100% Volume";
button.onclick = function(){iframe.contentWindow.postMessage({"volume":1.0}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Request Stats";
button.onclick = function(){iframe.contentWindow.postMessage({"getStats":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Request Loudness Levels";
button.onclick = function(){iframe.contentWindow.postMessage({"getLoudness":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Stop Sending Loudness Levels";
button.onclick = function(){iframe.contentWindow.postMessage({"getLoudness":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Start Recording";
button.onclick = function(){iframe.contentWindow.postMessage({"record":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Stop Recording";
button.onclick = function(){iframe.contentWindow.postMessage({"record":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Say Hello";
button.onclick = function(){iframe.contentWindow.postMessage({"sendChat":"Hello!"}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Send Keyframe";
button.onclick = function(){iframe.contentWindow.postMessage({"keyframe":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Insert Style Sheet";
var stylesheet = "#main { zoom: 0.5;} video {float: left; margin: 0; padding: 0; } #info {display:none;}";
button.onclick = function(){iframe.contentWindow.postMessage({"style":stylesheet}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "get StreamIDs and labels";
button.onclick = function(){iframe.contentWindow.postMessage({"getStreamIDs":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Start AutoMixer";
button.onclick = function(){iframe.contentWindow.postMessage({"automixer":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Stop AutoMixer";
button.onclick = function(){iframe.contentWindow.postMessage({"automixer":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "ENABLE TALLY LIGHT";
button.onclick = function(){iframe.contentWindow.postMessage({"sceneState":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "STOP TALLY LIGHT";
button.onclick = function(){iframe.contentWindow.postMessage({"sceneState":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Add Target Video";
button.onclick = function(){iframe.contentWindow.postMessage({"target":"*", "add":true, "settings":{"style":{"width":"640px", "height":"360px", "float":"left", "border":"10px solid red", "display":"block"}}}, '*');}; // target can be a stream ID or * for all.
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Remove Target Video";
button.onclick = function(){iframe.contentWindow.postMessage({"target":"*", "remove": true}, '*');}; // target can be a stream ID or * for all.
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "previewWebcam()";
button.onclick = function(){iframe.contentWindow.postMessage({"function":"previewWebcam"}, '*');}; // publishScreen
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Change to Camera #2";
button.onclick = function(){iframe.contentWindow.postMessage({"changeVideoDevice":2}, '*');}; // change text of add camera button
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Change to Microphone #4";
button.onclick = function(){iframe.contentWindow.postMessage({"changeAudioDevice":4}, '*');}; // change text of add camera button
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "eval('alert(\"DANGERUS\")'";
button.onclick = function(){iframe.contentWindow.postMessage({"function":"eval", "value":'alert(\"DANGERUS\")'}, '*');}; // publishScreen
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Change Add Camera text";
button.onclick = function(){iframe.contentWindow.postMessage({"function":"changeHTML", "target":"add_camera", "value":"NEW CAMERA TEXT"}, '*');}; // change text of add camera button
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "CLOSE IFRAME";
button.onclick = function(){iframeContainer.parentNode.removeChild(iframeContainer);};
iframeContainer.appendChild(button);
//////////// LISTEN FOR EVENTS
var h3 = document.createElement("h3");
h3.innerText = "The following commands are exclusive to the IFRAME API.";
iframeContainer.appendChild(h3);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
var button = document.createElement("button");
button.innerHTML = "Mute Speaker";
button.onclick = function(){iframe.contentWindow.postMessage({"mute":true}, '*');}; // "speaker" also works in the same way.
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Un-Mute Speaker";
button.onclick = function(){iframe.contentWindow.postMessage({"mute":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Toggle Speaker";
button.onclick = function(){iframe.contentWindow.postMessage({"mute":"toggle"}, '*');}; // open to a better suggestion here.
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Mute Mic";
button.onclick = function(){iframe.contentWindow.postMessage({"mic":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Un-Mute Mic";
button.onclick = function(){iframe.contentWindow.postMessage({"mic":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Toggle Mic";
button.onclick = function(){iframe.contentWindow.postMessage({"mic":"toggle"}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Mute Camera";
button.onclick = function(){iframe.contentWindow.postMessage({"camera":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Unmute Camera";
button.onclick = function(){iframe.contentWindow.postMessage({"camera":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Toggle Camera";
button.onclick = function(){iframe.contentWindow.postMessage({"camera":"toggle"}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Disconnect";
button.onclick = function(){iframe.contentWindow.postMessage({"close":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Low Bitrate";
button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":30}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "High Bitrate";
button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":5000}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Default Bitrate";
button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":-1}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Reload";
button.onclick = function(){iframe.contentWindow.postMessage({"reload":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "50% Volume";
button.onclick = function(){iframe.contentWindow.postMessage({"volume":0.5}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "100% Volume";
button.onclick = function(){iframe.contentWindow.postMessage({"volume":1.0}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Pan Left";
button.title = "Requires &panning to be added to the view link";
button.onclick = function(){iframe.contentWindow.postMessage({"panning":0}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Pan right";
button.title = "Requires &panning to be added to the view link";
button.onclick = function(){iframe.contentWindow.postMessage({"panning":180}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Pan Center";
button.title = "Requires &panning to be added to the view link";
button.onclick = function(){iframe.contentWindow.postMessage({"panning":90}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Request Loudness Levels";
button.onclick = function(){iframe.contentWindow.postMessage({"getLoudness":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Stop Sending Loudness Levels";
button.onclick = function(){iframe.contentWindow.postMessage({"getLoudness":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Start Recording";
button.onclick = function(){iframe.contentWindow.postMessage({"record":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Stop Recording";
button.onclick = function(){iframe.contentWindow.postMessage({"record":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Say Hello";
button.onclick = function(){iframe.contentWindow.postMessage({"sendChat":"Hello!"}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Send Keyframe";
button.onclick = function(){iframe.contentWindow.postMessage({"keyframe":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Insert Style Sheet";
var stylesheet = "#main { zoom: 0.5;} video {float: left; margin: 0; padding: 0; } #info {display:none;}";
button.onclick = function(){iframe.contentWindow.postMessage({"style":stylesheet}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "get StreamIDs and labels";
button.onclick = function(){iframe.contentWindow.postMessage({"getStreamIDs":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Start AutoMixer";
button.onclick = function(){iframe.contentWindow.postMessage({"automixer":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Stop AutoMixer";
button.onclick = function(){iframe.contentWindow.postMessage({"automixer":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "ENABLE TALLY LIGHT";
button.onclick = function(){iframe.contentWindow.postMessage({"sceneState":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "STOP TALLY LIGHT";
button.onclick = function(){iframe.contentWindow.postMessage({"sceneState":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Add Target Video";
button.onclick = function(){iframe.contentWindow.postMessage({"target":"*", "add":true, "settings":{"style":{"width":"640px", "height":"360px", "float":"left", "border":"10px solid red", "display":"block"}}}, '*');}; // target can be a stream ID or * for all.
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Remove Target Video";
button.onclick = function(){iframe.contentWindow.postMessage({"target":"*", "remove": true}, '*');}; // target can be a stream ID or * for all.
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "previewWebcam()";
button.onclick = function(){iframe.contentWindow.postMessage({"function":"previewWebcam"}, '*');}; // publishScreen
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Change to Camera #2";
button.onclick = function(){iframe.contentWindow.postMessage({"changeVideoDevice":2}, '*');}; // change text of add camera button
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Change to Microphone #4";
button.onclick = function(){iframe.contentWindow.postMessage({"changeAudioDevice":4}, '*');}; // change text of add camera button
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "eval('alert(\"DANGERUS\")'";
button.onclick = function(){iframe.contentWindow.postMessage({"function":"eval", "value":'alert(\"DANGERUS\")'}, '*');}; // publishScreen
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Change Add Camera text";
button.onclick = function(){iframe.contentWindow.postMessage({"function":"changeHTML", "target":"add_camera", "value":"NEW CAMERA TEXT"}, '*');}; // change text of add camera button
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "CLOSE IFRAME";
button.onclick = function(){iframeContainer.parentNode.removeChild(iframeContainer);};
iframeContainer.appendChild(button);
//////////// LISTEN FOR EVENTS
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if ("stats" in e.data){
var outputWindow = document.createElement("div");
console.log(e.data.stats);
if ("stats" in e.data){
var outputWindow = document.createElement("div");
console.log(e.data.stats);
var out = "<br />total_inbound_connections:"+e.data.stats.total_inbound_connections;
out += "<br />total_outbound_connections:"+e.data.stats.total_outbound_connections;
for (var streamID in e.data.stats.inbound_stats){
out += "<br /><br /><b>streamID:</b> "+streamID+"<br />";
out += printValues(e.data.stats.inbound_stats[streamID]);
}
outputWindow.innerHTML = out;
iframeContainer.appendChild(outputWindow);
var out = "<br />total_inbound_connections:"+e.data.stats.total_inbound_connections;
out += "<br />total_outbound_connections:"+e.data.stats.total_outbound_connections;
for (var streamID in e.data.stats.inbound_stats){
out += "<br /><br /><b>streamID:</b> "+streamID+"<br />";
out += printValues(e.data.stats.inbound_stats[streamID]);
}
if ("gotChat" in e.data){
outputWindow.innerHTML = out;
iframeContainer.appendChild(outputWindow);
}
if ("gotChat" in e.data){
var outputWindow = document.createElement("div");
outputWindow.innerHTML = e.data.gotChat.msg;
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
}
if ("action" in e.data){
var outputWindow = document.createElement("div");
outputWindow.innerHTML = "child-page-action: "+e.data.action+"<br />";
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
}
if ("streamIDs" in e.data){
var outputWindow = document.createElement("div");
outputWindow.innerHTML = "child-page-action: streamIDs<br />";
for (var key in e.data.streamIDs) {
outputWindow.innerHTML += "streamID: " + key + ", label:"+e.data.streamIDs[key] + "\n";
}
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
}
if ("loudness" in e.data){
console.log(e.data);
if (document.getElementById("loudness")){
outputWindow = document.getElementById("loudness");
} else {
var outputWindow = document.createElement("div");
outputWindow.innerHTML = e.data.gotChat.msg;
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
outputWindow.id = "loudness";
}
outputWindow.innerHTML = "child-page-action: loudness<br />";
for (var key in e.data.loudness) {
outputWindow.innerHTML += key + " Loudness: " + e.data.loudness[key] + "\n";
}
outputWindow.style.border="1px black";
if ("action" in e.data){
}
if ("sensors" in e.data){
console.log(e.data);
if (document.getElementById("sensors")){
outputWindow = document.getElementById("sensors");
} else {
var outputWindow = document.createElement("div");
outputWindow.innerHTML = "child-page-action: "+e.data.action+"<br />";
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
outputWindow.id = "sensors";
}
if ("streamIDs" in e.data){
var outputWindow = document.createElement("div");
outputWindow.innerHTML = "child-page-action: streamIDs<br />";
for (var key in e.data.streamIDs) {
outputWindow.innerHTML += "streamID: " + key + ", label:"+e.data.streamIDs[key] + "\n";
}
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
}
if ("loudness" in e.data){
console.log(e.data);
if (document.getElementById("loudness")){
outputWindow = document.getElementById("loudness");
} else {
var outputWindow = document.createElement("div");
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
outputWindow.id = "loudness";
}
outputWindow.innerHTML = "child-page-action: loudness<br />";
for (var key in e.data.loudness) {
outputWindow.innerHTML += key + " Loudness: " + e.data.loudness[key] + "\n";
}
outputWindow.style.border="1px black";
outputWindow.innerHTML = "child-page-action: sensors<br /><br />";
for (var key in e.data.sensors.lin) {
outputWindow.innerHTML += key + " linear: " + e.data.sensors.lin[key] + "<br />";
}
for (var key in e.data.sensors.acc) {
outputWindow.innerHTML += key + " acceleration: " + e.data.sensors.acc[key] + "<br />";
}
for (var key in e.data.sensors.gyro) {
outputWindow.innerHTML += key + " gyro: " + e.data.sensors.gyro[key] + "<br />";
}
for (var key in e.data.sensors.mag) {
outputWindow.innerHTML += key + " magnet: " + e.data.sensors.mag[key] + "<br />";
}
outputWindow.style.border="1px black";
if ("sensors" in e.data){
console.log(e.data);
if (document.getElementById("sensors")){
outputWindow = document.getElementById("sensors");
} else {
var outputWindow = document.createElement("div");
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
outputWindow.id = "sensors";
}
outputWindow.innerHTML = "child-page-action: sensors<br /><br />";
for (var key in e.data.sensors.lin) {
outputWindow.innerHTML += key + " linear: " + e.data.sensors.lin[key] + "<br />";
}
for (var key in e.data.sensors.acc) {
outputWindow.innerHTML += key + " acceleration: " + e.data.sensors.acc[key] + "<br />";
}
for (var key in e.data.sensors.gyro) {
outputWindow.innerHTML += key + " gyro: " + e.data.sensors.gyro[key] + "<br />";
}
for (var key in e.data.sensors.mag) {
outputWindow.innerHTML += key + " magnet: " + e.data.sensors.mag[key] + "<br />";
}
outputWindow.style.border="1px black";
}
});
}
});
function loadSelfCommands(){
var commands = {}
commands.speaker = function(value){sendSelfCommand("speaker",value)}; // "speaker" also works in the same way
commands.mic = function(value){sendSelfCommand("mic",value)};
commands.camera = function(value){sendSelfCommand("camera",value)};
commands.bitrate = function(value){sendSelfCommand("bitrate",value)};
commands.volume = function(value){sendSelfCommand("volume",value)};
commands.record = function(value){sendSelfCommand("record",value)};
commands.sayHello = function(value){sendSelfCommand("sendChat","Hello")};
var target_self = document.body;
var hr = document.createElement("hr");
target_self.appendChild(hr);
var h3 = document.createElement("h3");
h3.innerText = "The following commands re-use the Companion.Ninja HTTP/WSS API, except you can send them via this Iframe interface.";
target_self.appendChild(h3);
var a = document.createElement("a");
a.innerText = "More details of the below IFRAME API details are here: https://github.com/steveseguin/Companion-Ninja#api-commands";
a.href = "https://github.com/steveseguin/Companion-Ninja#api-commands";
a.target = "_blank";
target_self.appendChild(a);
var hr = document.createElement("hr");
target_self.appendChild(hr);
for (var k in commands) {
var button = document.createElement("button");
button.innerHTML = k + ":<br />TRUE";
button.dataset.command = k;
button.onclick = function(){commands[this.dataset.command](true);}
target_self.appendChild(button);
var button = document.createElement("button");
button.innerHTML = k + ":<br />FALSE";
button.dataset.command = k;
button.onclick = function(){commands[this.dataset.command](false);}
target_self.appendChild(button);
} // list available commands to console
commands.reload = function(){sendSelfCommand("reload",true);};
commands.hangup = function(){sendSelfCommand("hangup",true);};
var button = document.createElement("button");
button.innerHTML = k + ":<br />TRUE";
button.onclick = function(){commands["reload"](true);}
target_self.appendChild(button);
var button = document.createElement("button");
button.innerHTML = k + ":<br />TRUE";
button.onclick = function(){commands["hangup"](true);}
target_self.appendChild(button);
return commands;
}
function loadGuestCommands(guestid, streamID=false){ // this is the same as the Companion API, but can be issued to the IFRAME API
var container = document.createElement("div");
container.id = "guest_"+guestid+"_container";
document.body.appendChild(container);
var hr = document.createElement("hr");
container.appendChild(hr);
var h3 = document.createElement("h3");
if (streamID){
h3.innerHTML = "These target guest with the streamID: <i>"+guestid+ "</i> (if a director)";
} else {
h3.innerText = "These target the guest in slot "+guestid+ " (if you are director)";
}
container.appendChild(h3);
var button = document.createElement("button");
button.innerHTML = "transfer popup";
button.onclick = function(){sendGuestCommand(guestid, "forward");};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "transfer to 'room321'";
button.onclick = function(){sendGuestCommand(guestid, "forward", 'room321');};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 1";
button.onclick = function(){sendGuestCommand(guestid, "addScene");}; /// SCENE 1 or specify a custom scene name as a value
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "mute in scene";
button.onclick = function(){sendGuestCommand(guestid, "muteScene");};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "mute everywhere";
button.onclick = function(){sendGuestCommand(guestid, "mic");};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "hang up";
button.onclick = function(){sendGuestCommand(guestid, "hangup");};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "solo chat";
button.onclick = function(){sendGuestCommand(guestid, "soloChat");};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "remote speaker";
button.onclick = function(){sendGuestCommand(guestid, "speaker");};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "remote display";
button.onclick = function(){sendGuestCommand(guestid, "display");};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "rainbow puke fix";
button.onclick = function(){sendGuestCommand(guestid, "forceKeyframe");};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "highlight";
button.onclick = function(){sendGuestCommand(guestid, "soloVideo");};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 2";
button.onclick = function(){sendGuestCommand(guestid, "addScene", 2);};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 3";
button.onclick = function(){sendGuestCommand(guestid, "addScene", 3);};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 4";
button.onclick = function(){sendGuestCommand(guestid, "addScene", 4);};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 5";
button.onclick = function(){sendGuestCommand(guestid, "addScene", 5);};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 6";
button.onclick = function(){sendGuestCommand(guestid, "addScene", 6);};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = " scene 7";
button.onclick = function(){sendGuestCommand(guestid, "addScene", 7);};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 8";
button.onclick = function(){sendGuestCommand(guestid, "addScene", 8);};
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 'test'";
button.onclick = function(){sendGuestCommand(guestid, "addScene", 'test');}; // specifying a custom scene; it needs to be active for this to work..
container.appendChild(button);
var input = document.createElement("label");
input.innerHTML = "mic volume:";
container.appendChild(input);
var input = document.createElement("input");
input.type = "range";
input.title = "volume";
input.min = 0;
input.max = 200;
input.value = 100;
input.onchange = function(){sendGuestCommand(guestid, "volume", this.value);};
container.appendChild(input);
}
function sendGuestCommand(target, action, value=null){ //
iframe.contentWindow.postMessage({"target":target, "action":action, "value":value}, '*'); //
//sendMessage(JSON.stringify({"target":target, "action":action, "value":value})); // if using the Companion API..
}
function sendSelfCommand(action, value=null){
iframe.contentWindow.postMessage({"target":null, "action":action, "value":value}, '*');
//sendMessage(JSON.stringify({"target":target, "action":action, "value":value})); // if using the Companion API..
}
loadSelfCommands();
loadGuestCommands(1);
loadGuestCommands(2);
loadGuestCommands(3);
var randomGuest = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10);
loadGuestCommands(randomGuest, true);
}
@ -394,10 +616,10 @@ function printValues( obj) {
</head>
<body>
<input placeholder="Enter an OBS.Ninja View URL here" id="viewlink" />
<input placeholder="Enter an VDO.Ninja View URL here" id="viewlink" />
<button onclick="loadIframe();">ADD</button>
<input type="checkbox" id="clean" checked>Clean Output
<input type="checkbox" id="transparent" checked>Transparent
<input type="checkbox" id="clean" >Clean Output
<input type="checkbox" id="transparent">Transparent
<input type="checkbox" id="hidemenu">Hide Menu
<div id="container">

View File

@ -22,7 +22,7 @@
<link rel="icon" type="image/png" sizes="32x32" href="./media/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="./media/favicon-16x16.png" />
<link rel="icon" href="./media/favicon.ico" />
<link itemprop="thumbnailUrl" href="./media/obsNinja_logo_full.png" />
<link itemprop="thumbnailUrl" href="./media/vdoNinja_logo_full.png" />
<!-- Primary Meta Tags -->
<title>VDO.Ninja</title>
<meta name="title" content="VDO.Ninja" />
@ -34,8 +34,8 @@
<meta property="og:url" content="https://vdo.ninja/" />
<meta property="og:title" content="VDO.Ninja" />
<meta property="og:description" content="Bring live video from your smartphone, computer, or friends directly into Studio. 100% free." />
<meta property="og:image" itemprop="image" content="https://vdo.ninja/media/obsNinja_logo_full.png" />
<meta name="msapplication-TileImage" content="./media/obsNinja_logo_full.png" />
<meta property="og:image" itemprop="image" content="https://vdo.ninja/media/vdoNinja_logo_full.png" />
<meta name="msapplication-TileImage" content="./media/vdoNinja_logo_full.png" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
@ -44,7 +44,7 @@
<meta property="twitter:url" content="https://vdo.ninja/" />
<meta property="twitter:title" content="VDO.Ninja" />
<meta property="twitter:description" content="Bring live video from your smartphone, computer, or friends directly into OBS Studio. 100% free." />
<meta property="twitter:image" content="./media/obsNinja_logo_full.png" />
<meta property="twitter:image" content="./media/vdoNinja_logo_full.png" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<style>
@ -54,21 +54,20 @@
transition: opacity .1s linear;
}
</style>
<link rel="stylesheet" href="./lineawesome/css/line-awesome.min.css" />
<link rel="stylesheet" href="./main.css?ver=69" />
<link rel="stylesheet" href="./main.css?ver=125" />
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/adapter.min.js"></script>
<style id="lightbox-animations" type="text/css"></style>
</head>
<body id="main" class="hidden">
<span itemprop="image" itemscope itemtype="image/png">
<link itemprop="url" href="./media/obsNinja_logo_full.png" />
<link itemprop="url" href="./media/vdoNinja_logo_full.png" />
</span>
<link itemprop="thumbnailUrl" href="./media/obsNinja_logo_full.png" />
<link itemprop="thumbnailUrl" href="./media/vdoNinja_logo_full.png" />
<span itemprop="thumbnail" itemscope itemtype="http://schema.org/ImageObject">
<link itemprop="url" href="./media/obsNinja_logo_full.png" />
<link itemprop="url" href="./media/vdoNinja_logo_full.png" />
</span>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=33"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=214"></script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=34"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=291"></script>
<input id="zoomSlider" type="range" style="display: none;" />
<div id="header">
@ -86,15 +85,16 @@
<font style="color: #888;" id="copythisurl"> &nbsp;
<span data-translate="copy-this-url">Copy this URL into an OBS "Browser Source"</span> <i style="color: #CCC;" class="las la-long-arrow-alt-right"></i> &nbsp;
</font>
</div>
<div id="head3a" style="display: inline-block;" class="advanced">
<a
id="reshare"
data-drag="1"
onclick="popupMessage(event);copyFunction(this)"
onclick="copyFunction(this, event)"
class="task grabLinks"
onmousedown="copyFunction(this)"
style="font-weight: bold; color: #afa !important; cursor: grab; background-color: #0000; font-size: 115%; min-width: 335px; max-width: 800px;"
></a>
<i class="las la-paperclip" style="color: #DDD;" onclick="popupMessage(event);copyFunction(document.getElementById('reshare'));" onmouseover="this.style.cursor='pointer'"></i>
<i class="las la-paperclip" style="color: #DDD;" onclick="copyFunction(document.getElementById('reshare'), event);" onmouseover="this.style.cursor='pointer'"></i>
</div>
<div id="head4" style="display: inline-block;" class="advanced">
<font style="font-size: 68%; color: white;">
@ -118,6 +118,12 @@
<i id="queuetoggle" class="toggleSize las la-stream my-float"></i>
<div id="queueNotification"></div>
</div>
<div id="sharefilebutton" title="Transfer any file to the group" alt="Transfer any file to the group" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="toggleFileshare()" tabindex="16" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float" style="cursor: pointer;" >
<i id="filesharetoggle" class="toggleSize las la-file-upload my-float"></i>
<div id="transferNotification"></div>
</div>
<div id="chatbutton" title="Toggle the Chat" alt="Toggle the Chat" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="toggleChat()" tabindex="16" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="advanced float" style="cursor: pointer;" >
<i id="chattoggle" class="toggleSize las la-comment-alt my-float"></i>
<div id="chatNotification"></div>
@ -137,7 +143,7 @@
<div id="screenshare2button" onmousedown="event.preventDefault(); event.stopPropagation();" title="Create a Secondary Stream" alt="Create a Secondary Stream" onclick="createIframePopup()" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float advanced" style="cursor: pointer;">
<i id="screenshare2toggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-tv my-float"></i>
</div>
<div id="websitesharebutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a website as an embedded iFRAME" alt="Share a website as an embedded iFRAME" onclick="shareWebsite()" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float advanced" style="cursor: pointer;">
<div id="websitesharebutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a website as an embedded iFRAME" alt="Share a website as an embedded iFRAME" onclick="shareWebsite(false, event)" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float advanced" style="cursor: pointer;">
<i id="websitesharetoggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-window-maximize my-float"></i>
</div>
@ -266,7 +272,7 @@
</tr><tr>
<th></th>
<th style="text-align:right;">
<button onclick="createRoom()" class="gobutton" style="float: left;" alt="Enter the room as the group's director" title="You'll enter as the room's director">
<button onclick="createRoom()" class="gobutton" style="float: left;width:100%;" alt="Enter the room as the group's director" title="You'll enter as the room's director">
<span data-translate="enter-the-rooms-control">Enter the Room's Control Center</span>
</button>
<br /><br />
@ -311,10 +317,15 @@
<button onclick="this.disabled=true;setTimeout(function(){requestBasicPermissions();},20);" id="getPermissions" style="display:none;" data-ready="false" >
<span data-translate="ask-for-permissions">Allow Access to Camera/Microphone</span>
</button>
<button onclick="publishWebcam(this)" title="start streaming" tabindex="15" id="gowebcam" class="gowebcam" alt="Start Streaming" disabled data-audioready="false" data-ready="false" >
<span data-translate="waiting-for-camera">Waiting for Camera to Load</span>
</button>
<br />
<span style="display:block;">
<button onclick="publishWebcam(this)" title="start streaming" tabindex="15" id="gowebcam" class="gowebcam" alt="Start Streaming" disabled data-audioready="false" data-ready="false" >
<span data-translate="waiting-for-camera">Waiting for Camera to Load</span>
</button>
</span>
<div id="consentWarning" class="startupWarning advanced">
<i class="las la-exclamation-circle"></i>
<p><span data-translate="privacy-disabled">Privacy warning: The director will be able to remotely change your camera and microphone.</span></p>
</div>
<span id="guestTips" style="display:none">
<p>For the best possible experience, make sure</p>
<span><i class="las la-plug"></i><span>Your device is powered</span></span>
@ -369,14 +380,14 @@
</ul>
</div>
<br />
<span id="headphonesDiv" style="text-align:left; margin:17px 0 0 0; max-width: 550px; min-width: 420px; background-color: #f3f3f3; display: none; padding: 10px 10px; border: 1px solid #ccc; vertical-align: middle;">
<span id="headphonesDiv" class="audioMenu">
<div class="audioTitle2">
<i class="las la-headphones"></i><span data-translate="select-output-source"> Audio Output Destination:
</span><button onclick="playtone()" class="white" style="margin:0 0 0 15px;" type="button">Test</button></div>
<select id="outputSource" ></select>
</span>
<div id="effectsDiv" style="user-select: none; font-size:100%; text-align:left; margin: 17px auto; max-width:450px; min-width: 420px; background-color: #f3f3f3; display: none; padding: 10px 10px; border: 1px solid #ccc; vertical-align: middle;">
<div id="effectsDiv">
<div style="text-align: left;display: inline-block;">
<i class="las la-robot"></i><span data-translate="select-digital-effect"> Digital Video Effects: </span>
</div>
@ -385,8 +396,11 @@
<option value="3" data-translate="blurred-background">Blurred background</option>
<option value="4" data-translate="digital-greenscreen">Digital greenscreen</option>
<option value="5" data-translate="virtual-background">Virtual background</option>
<option class="advanced" value="6" data-translate="face-mesh">Face mesh (slow load)</option>
<option class="advanced" value="anon" data-translate="anonymous-mask">Anonymous mask</option>
<option class="advanced" value="dog" data-translate="dog-face">Dog ears and nose</option>
</select>
<span data-warnSimdNotice="true" style='display:none; font-size: 140%; margin-left:10px; vertical-align: middle; cursor:pointer' title="Improve performance and quality with this tip" onclick="warnUser(`For improved performance, use Chrome v87 or newer with SIMD support enabled.<br />Enable SIMD here: <a href='chrome://flags/#enable-webassembly-simd' target='_blank' onclick='popupMessage(event);copyFunction(this)' >chrome://flags/#enable-webassembly-simd</a>`);">
<span data-warnSimdNotice="true" style='display:none; font-size: 140%; margin-left:10px; vertical-align: middle; cursor:pointer' title="Improve performance and quality with this tip" onclick="warnUser(`For improved performance, use Chrome v87 or newer with SIMD support enabled.<br />Enable SIMD here: <a href='chrome://flags/#enable-webassembly-simd' target='_blank' onclick='copyFunction(this,event)' >chrome://flags/#enable-webassembly-simd</a>`);">
<i class="las la-info-circle"></i>
</span>
<span data-effectsNotice="true" style='display:none; font-size: 140%; margin-left:10px; vertical-align: middle; cursor:pointer' title="Improve performance and quality with this tip" onclick="warnUser('Use a Chromium Based Browser');">
@ -403,19 +417,7 @@
</div>
</div>
<span id="addPasswordBasic" style="width: 450px;
white-space: nowrap;
overflow: hidden;
border-bottom: 0;
display: block;
text-align: left;
margin: 17px auto 0 auto;
max-width: 450px;
min-width: 420px;
background-color: #f3f3f3;
padding: 10px 10px;
border: 1px solid #ccc;
vertical-align: middle;">
<span id="addPasswordBasic" >
<i class="las la-key"></i><span data-translate="add-a-password"> Add a Password:</span>
<input type="text" id="passwordBasicInput" placeholder="optional" style="border: solid 1px #AAA;
padding: 4px 6px;
@ -423,12 +425,14 @@
margin: 0 6px;"/>
</span>
<div id="SafariWarning">
<div id="SafariWarning" class="startupWarning advanced">
<i class="las la-exclamation-circle"></i>
<p><span data-translate="use-chrome-instead">Consider using a Chromium-based browser instead.<br />
Safari is more prone to having audio issues</span></p>
</div>
</div>
<div class="outer close">
<div class="inner">
@ -455,7 +459,7 @@
<i class="las la-cog" style="font-size: 170%; vertical-align: middle;" aria-hidden="true"></i>
</span>
<center>
<span id="videoSettings2" style="margin: auto auto; display: none; background-color: white; vertical-aligh: middle; border: 3px solid #ccc; max-width: 500px; padding: 10px 10px 5px 10px; margin: 10px 0 5px 0;">
<span id="videoSettings2" style="margin: auto auto; display: none; background-color: white; vertical-aligh: middle; border: 3px solid #ccc; max-width: 500px; padding: 10px 10px 5px 10px; margin: 0px 0px 10px;">
<form id="webcamquality2">
<input type="radio" id="fullhd2" name="resolution2" value="0" />
<label for="fullhd">
@ -474,8 +478,11 @@
<div id="webcamstats2" style="padding: 5px 0 0 0;"></div>
</form>
</span>
<br />
</center>
<div id="consentWarning2" class="startupWarning advanced">
<i class="las la-exclamation-circle"></i>
<p><span data-translate="privacy-disabled">Privacy warning: The director will be able to remotely access your camera and microphone if you continue.</span></p>
</div>
<p id="audioScreenShare1">
<i class="las la-microphone-alt"></i>
<span data-translate="audio-sources">Audio Sources</span>
@ -489,12 +496,10 @@
</select>
</p>
<br />
<span id="headphonesDiv2" style="background-color: #f3f3f3; min-width: 270px; display: none; padding: 5px 10px; border: 1px solid #ccc; vertical-align: middle;">
<span id="headphonesDiv2">
<i class="las la-headphones"></i>
<span data-translate="select-output-source"> Audio Output Destination: <button onclick="playtone(true)" class="white" style="padding:3px 5px 2px 5px; margin:0; margin-left:15px; position: relative; " type="button">Test</button></span>
<br />
<select id="outputSourceScreenshare" style="background-color: #FFF; padding:10px 5px; min-width: 268px; display:inline-block; vertical-align: middle;" onclick="requestOutputAudioStream();">
<select id="outputSourceScreenshare" style="background-color: #FFF; margin-top:5px; padding:10px 5px; width:100%; display:inline-block; vertical-align: middle;" onclick="requestOutputAudioStream();">
<option value="default">
Default Device
</option>
@ -541,54 +546,60 @@
<div class="invite_setting_item">
<input type="checkbox" id="invite_bitrate" />
<label for="invite_bitrate">
<span data-translate="unlock-video-bitrate" title="Ideal for 1080p60 gaming, if your computer and upload are up for it" >Unlock Video Bitrate (20mbps)</span>
<span data-translate="unlock-video-bitrate" title="Ideal for 1080p60 gaming, if your computer and upload are up for it" >Unlock the video bitrate (20mbps)</span>
</label>
</div>
<div class="invite_setting_item">
<input type="checkbox" id="invite_vp9" />
<label for="invite_vp9">
<span data-translate="force-vp9-video-codec" title="Better video compression and quality at the cost of increased CPU encoding load">Force VP9 Video Codec</span>
<span data-translate="force-vp9-video-codec" title="Better video compression and quality at the cost of increased CPU encoding load">Use the VP9 video codec</span>
</label>
</div>
<div class="invite_setting_item">
<input type="checkbox" id="invite_stereo" />
<label for="invite_stereo">
<span data-translate="enable-stereo-and-pro" title="Disable digital audio-effects and increase audio bitrate">Enable Stereo and Pro HD Audio</span>
<span data-translate="enable-stereo-and-pro" title="Disable digital audio-effects and increase audio bitrate">Enable stereo and pro HD audio</span>
</label>
</div>
<div class="invite_setting_item">
<label for="invite_quality" data-translate="video-resolution">Video Resolution: </label>
<label for="invite_quality" data-translate="video-resolution">Video resolution: </label>
<select id="invite_quality" name="invite_quality">
<option value="-1" selected>User Selectable</option>
<option value="0">Maximum Resolution</option>
<option value="-1" selected>User selectable</option>
<option value="0">Maximum resolution</option>
<option value="1">Balanced</option>
<option value="2">Smooth and Cool</option>
<option value="2">Smooth and cool</option>
</select>
</div>
</div>
<div class="invite_setting_group">
<div class="invite_setting_item">
<input type="checkbox" id="invite_effects" />
<label for="invite_effects">
<span data-translate="allow-effects-invite" title="The guest will be able to select digital video effects to apply.">Allow video effects to be used</span>
</label>
</div>
<div class="invite_setting_item">
<input type="checkbox" id="invite_automic" />
<label for="invite_automic">
<span data-translate="hide-mic-selection" title="The guest will not have a choice over audio-options">Force Default Microphone</span>
<span data-translate="hide-mic-selection" title="The guest will not have a choice over audio-options">Force select the default microphone</span>
</label>
</div>
<div class="invite_setting_item">
<input type="checkbox" id="invite_hidescreen" />
<label for="invite_hidescreen">
<span data-translate="hide-screen-share" title="The guest will only be able to select their webcam as an option">Hide Screenshare Option</span>
<span data-translate="hide-screen-share" title="The guest will only be able to select their webcam as an option">Hide the screenshare option</span>
</label>
</div>
<div class="invite_setting_item">
<input type="checkbox" id="invite_remotecontrol" />
<label for="invite_remotecontrol">
<span data-translate="allow-remote-control" title="Hold CTRL and the mouse wheel to zoom in and out remotely of compatible video streams">Remote Control Camera Zoom (android)</span>
<span data-translate="allow-remote-control" title="Hold CTRL and the mouse wheel to zoom in and out remotely of compatible video streams">Remote control of zoom (android)</span>
</label>
</div>
<div class="invite_setting_item">
<input type="checkbox" id="invite_obfuscate" />
<label for="invite_obfuscate">
<span data-translate="obfuscate_url" title="Encode the URL so that it's harder for a guest to modify the settings.">Obfuscate the Invite URL</span>
<span data-translate="obfuscate_url" title="Encode the URL so that it's harder for a guest to modify the settings.">Obfuscate the invite URL</span>
</label>
</div>
<div class="invite_setting_item">
@ -647,6 +658,9 @@
<h1>File Sharing seems to be broken on Chrome v88.</h1>
<p>Try <a href="https://github.com/aws/amazon-chime-sdk-js/issues/1031">turning off hardware-accleration</a> in Chrome/Edge to fix the issue, or maybe use a different browser.</p>
</div>
<br /><br />
To host a file for download, rather than for streaming, try the following instead:
<input id="fileselector2" onchange="session.hostFile(this,event);" type="file" title="Transfer any file"/>
</div>
<div class="outer close">
@ -721,7 +735,7 @@
Some devices that use H264 hardware encoding can experience video glitching; switching to VP8 or VP9 as a codec can help.
</li>
<li>
If using multiple group scenes at a time, iOS devices may fail to work if the hardware encoders max out. Perhaps try VP8 as a codec instead.
Video glitching and random audio-loss can occur when using the OBS browser source. The <a href='https://github.com/steveseguin/electroncapture/blob/master/README.md'>Electron Capture app</a> avoids these issues.
</li>
<li>
A list of less common issues can <a href="https://docs.vdo.ninja/common-errors-and-known-issues/known-issues-browser-bugs-and-more">be found here</a>.
@ -731,7 +745,7 @@
👋 👀 Welcome to VDO Ninja! We've rebranded! 📼 Nothing else is changing and we're staying 100% free.
</h4>
<br />
🌻 Site Updated on July 8th. The <a href="https://docs.vdo.ninja/release-notes/v18_3">v18.3 release notes are here</a>. If new issues occur, the previous version can also be <a href="https://vdo.ninja/v18/">found here</a>.
🎁 Site updated September 7th. The <a href="https://docs.vdo.ninja/release-notes/v19">v19.0 release notes are here</a>. If new issues occur, the previous version can be <a href="https://vdo.ninja/v183/">found here</a>.
<br />
<br />
@ -747,14 +761,7 @@
<input type="submit" />
</form>
<div id="credits" class="credits">
Icons made by
<a href="https://www.flaticon.com/authors/lucy-g" >Lucy G</a> from
<a href="https://www.flaticon.com/" >www.flaticon.com</a> is licensed by
<a href="https://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a> and by
<a href="https://www.flaticon.com/authors/gregor-cresnar">Gregor Cresnar</a> from
<a href="https://www.flaticon.com/" >www.flaticon.com</a>
<a href='https://github.com/steveseguin/vdoninja'>VDO.Ninja, by Steve Seguin</a>
</div>
</div>
<span id="electronDragZone" style="pointer-events: none; z-index:-10; position:absolute;top:0;left:0;width:100%;height:5%;-webkit-app-region: drag;min-height:33px;"></span>
@ -802,7 +809,7 @@
<div class='directorBlock'>
<h2 title="Invite a guest or camera source to publish into the group room" style="margin-top: 5px;"><i class="las la-video director-link-icons" ></i> INVITE A GUEST</h2>
<span style="margin:5px; line-height: 1.6;" data-translate='invite-users-to-join'>Guests can use the link to join the group room</span>
<a onclick='popupMessage(event);copyFunction(this)' id="director_block_1" onmousedown='copyFunction(this)' class='task grabLinks' style='cursor:copy;background-color: #0003;'></a>
<a onclick='copyFunction(this,event)' id="director_block_1" class='task grabLinks' style='cursor:copy;background-color: #0003;'></a>
<span style="display:block;">
<span style="bottom: 0; margin: 0 0 0 10px; top: 22px; position: relative;">
<label class="switch" title="If disabled, the invited guest will not be able to see or hear anyone in the room.">
@ -811,7 +818,7 @@
</label>
<span data-translate="guests-hear-others">Guests hear others</span>
</span>
<button class='pull-right grey' style='font-size:1.15em' onclick='popupMessage(event);copyFunction(getById("director_block_1"))'><i class='las la-copy'></i>Copy link</button>
<button class='pull-right grey' style='font-size:1.15em' onclick='copyFunction(getById("director_block_1"),event)'><i class='las la-copy'></i>Copy link</button>
<button class='pull-right grey' style='font-size:1.15em' id="showCustomizerButton1" onclick='showCustomizer(1,this)'><i class='las la-tools'></i>Customize</button>
<span>
</div>
@ -821,7 +828,7 @@
<div class='directorBlock' style="background-color: var(--green-accent);" >
<h2 title="Use this link in the OBS Browser Source to capture the video or audio" style="margin-left: 1px;margin-top: 5px;"><i class="las la-th-large director-link-icons" style="margin-right: 6px;" ></i> <span data-translate="capture-a-group-scene">CAPTURE A GROUP SCENE</span></h2>
<span style="margin:5px; line-height: 1.6;" data-translate='this-is-obs-browser-source-link'>Use in OBS or other studio software to capture the group video mix</span>
<a onclick='popupMessage(event);copyFunction(this)' id="director_block_3" onmousedown='copyFunction(this)' class='task grabLinks' style='cursor:copy;background-color: #0003;'></a>
<a onclick='copyFunction(this,event)' id="director_block_3" class='task grabLinks' style='cursor:copy;background-color: #0003;'></a>
<span style="display:block;">
<span style="bottom: 0; margin: 0 0 0 10px; top: 22px; position: relative;">
<label class="switch" title="If disabled, you must manually add a video to a scene for it to appear.">
@ -830,7 +837,7 @@
</label>
<span data-translate="auto-add-guests">Auto-add guests</span>
</span>
<button class='pull-right grey' style='font-size:1.15em' onclick='popupMessage(event);copyFunction(getById("director_block_3"))'><i class='las la-copy'></i>Copy link</button>
<button class='pull-right grey' style='font-size:1.15em' onclick='copyFunction(getById("director_block_3"),event)'><i class='las la-copy'></i>Copy link</button>
<button class='pull-right grey' style='font-size:1.15em' id="showCustomizerButton3" onclick='showCustomizer(3,this)'><i class='las la-tools'></i>Customize</button>
<span>
</div>
@ -863,7 +870,7 @@
<input type="checkbox" data-param="&sticky" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
<span data-translate="remote-monitoring">Invite saved to cookie</span>
<span data-translate="invite-saved-to-cookie">Invite saved to cookie</span>
<Br />
<label class="switch" title="Guest will be prompted to enter a Display Name">
@ -905,19 +912,17 @@
</label>
<span data-translate="auto-select-camera">Auto-select default camera</span>
<Br />
<label class="switch" title="The camera will load in a default safe-mode that may work if other modes fail.">
<input type="checkbox" data-param="&safemode" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
<span data-translate="compatibility-mode">Compatibility mode</span>
<Br />
<label class="switch" title="The guest won't have access to changing camera settings or screenshare">
<input type="checkbox" data-param="&ns" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
<span data-translate="hide-setting-buttons">Hide settings button</span>
<Br />
<label class="switch" title="The guest's self-video preview will appear tiny in the top right">
<input type="checkbox" data-param="&mini" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
<span data-translate="mini-self-preview">Mini self-preview</span>
<Br />
<label class="switch" title="Allow the guests to pick a virtual backscreen effect">
<input type="checkbox" data-param="&effects" onchange="updateLink(1,this);">
@ -926,12 +931,11 @@
<font class="tooltip" style='cursor: help;position:relative;bottom:2px;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;'><span class="tooltiptext">Uses more CPU and freezes the video if the guest doesn't keep the tab visible.</span></font> <span data-translate="virtual-backgrounds">Virtual backgrounds</span>
<br />
<label class="switch" title="Videos use an animated transition when being remixed">
<input type="checkbox" data-param="&animate" onchange="updateLink(1,this);">
<label class="switch" title="Disable animated transitions during video mixing">
<input type="checkbox" data-param="&animate=0" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
<span data-translate="animate-mixing">Animate mixing</span>
<span data-translate="disable-animated-mixing">Disable animations</span>
</div>
<div style="display:inline-block;margin-top: 12px; position: relative; margin-right:10px;">
@ -982,8 +986,16 @@
<input type="checkbox" data-param="&webp" onchange="updateLinkWebP(1,this);">
<span class="slider"></span>
</label>
<font class="tooltip" style='cursor: help;position:relative;bottom:2px;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;'><span class="tooltiptext">You must keep the director's tab open and visible for this to work, or use the electron capture app.</span></font>
<font class="tooltip" style='cursor: help;position:relative;bottom:2px;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;'><span class="tooltiptext">Pretty experimental and has Lo-Fi quality, though relatively low CPU usage.</span></font>
<span data-translate="low-cpu=broadcast-codec">Low-CPU broadcast codec</span>
<Br />
<label class="switch" title="The guest's self-video preview will appear tiny in the top right">
<input type="checkbox" data-param="&mini" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
<span data-translate="mini-self-preview">Mini self-preview</span>
<Br />
<label class="switch" title="The guest can only see the Director's video, if provided">
<input type="checkbox" data-param="&broadcast" id="broadcastSlider" onchange="updateLink(1,this);">
@ -1134,7 +1146,8 @@
<h4 style='color:#CCC;margin:20px 20px 0 20px;' data-translate='more-than-four-can-join' >These four guest slots are just for demonstration. More than four guests can actually join a room.</h4>
</div></div>
</div>
<div id="hiddenElements"></div>
<div id="overlayClockContainer" data-initial="600" class="advanced"><span id="overlayClock"></span></div>
<div id="overlayMsgs" onclick="function(e){e.target.innerHTML = '';}" style="display:none"></div>
<div id="bigPlayButton" onclick="function(e){e.target.innerHTML = '';}" style="display:none"></div>
<div id="controls_blank" style="display: none;">
@ -1169,9 +1182,10 @@
</button>
<!---- /////// BREAK //////// -->
<hr /><span style="user-select: none;grid-column: 1;width:100%;margin:5px 0 ;font-size:80%; cursor: pointer;" onclick="toggleByDataset('1');getById('chevarrow3').classList.toggle('bottom');getById('chevarrow3').classList.toggle('right');"><i id="chevarrow3" style="padding:0px 7px 0 3px;" class="chevron right" aria-hidden="true"></i><span data-translate="More-scene-options">More scene options</span></span>
<span class="hideDropMenu" onclick="toggleByDataset('1');getById('chevarrow3').classList.toggle('bottom');getById('chevarrow3').classList.toggle('right');"><i id="chevarrow3" style="padding:0px 7px 0 3px;" class="chevron right" aria-hidden="true"></i><span data-translate="More-scene-options">More scene options</span></span>
<span class="hideDropMenu" style="grid-column: 2;"></span>
<button data-action-type="addToScene" class="hidden" data-cluster="1" style="grid-column: 1;" data-scene="2" title="Add this Video to any remote '&scene=2'" onclick="directEnable(this, event, 2);">
<button data-action-type="addToScene" class="hidden" data-cluster="1" data-scene="2" title="Add this Video to any remote '&scene=2'" onclick="directEnable(this, event, 2);">
<i class="las la-plus-square" style="color:#060"></i>
<span data-translate="add-to-scene2">add to scene 2</span>
</button>
@ -1181,7 +1195,7 @@
<span data-translate="mute-scene" >mute in scene</span>
</button>
<span class="hidden" data-cluster="1" >
<span class="hidden" data-cluster="1" data-action-type="sceneCluster1">
<button style="width: 35.2px" data-action-type="addToScene" data-scene="3" data-action-type="add-scene-3" title="Add to Scene 3" onclick="directEnable(this, event, 3);">
<span >S3</span>
</button>
@ -1193,7 +1207,7 @@
</button>
</span>
<span class="hidden" data-cluster="1">
<span class="hidden" data-cluster="1" data-action-type="sceneCluster2">
<button style="width: 35.2px" data-action-type="addToScene" data-scene="6" data-action-type="add-scene-6" title="Add to Scene 6" onclick="directEnable(this, event, 6);">
<span >S6</span>
</button>
@ -1205,7 +1219,7 @@
</button>
</span>
<button class="hidden" data-cluster="1" data-action-type="force-keyframe" style="grid-column: 1; background-image: linear-gradient(90deg, #C9F0FF 0%, #FFDFB9 39%, #FFDFDF 70%, #D9FFEC 100%);" data-value="0" title="Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues." onclick="requestKeyframeScene(this, event);">
<button class="hidden" data-cluster="1" data-action-type="force-keyframe" style=" background-image: linear-gradient(90deg, #C9F0FF 0%, #FFDFB9 39%, #FFDFDF 70%, #D9FFEC 100%);" data-value="0" title="Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues." onclick="requestKeyframeScene(this);">
<span data-translate="force-keyframe">Rainbow Puke Fix</span>
</button>
@ -1215,10 +1229,11 @@
</button>
<!---- /////// BREAK //////// -->
<hr /><span style="user-select: none;grid-column: 1;width:100%;margin:5px 0 ;font-size:80%;cursor: pointer;" onclick="toggleByDataset('2');getById('chevarrow4').classList.toggle('bottom');getById('chevarrow4').classList.toggle('right');"><i id="chevarrow4" class="chevron right" aria-hidden="true" style="padding:0px 7px 0 3px;" ></i><span data-translate="additional-controls">Additional controls</span></span>
<span class="hideDropMenu" onclick="toggleByDataset('2');getById('chevarrow4').classList.toggle('bottom');getById('chevarrow4').classList.toggle('right');"><i id="chevarrow4" class="chevron right" aria-hidden="true" style="padding:0px 7px 0 3px;" ></i><span data-translate="additional-controls">Additional controls</span></span>
<span class="hideDropMenu" style="grid-column: 2;" ></span>
<button class="hidden" data-cluster="2" data-action-type="solo-video" style="grid-column: 1; text-shadow: 0px 0px yellow;" data-value="0" title="Solo this video everywhere" onclick="requestInfocus(this);">
<button class="hidden" data-cluster="2" data-action-type="solo-video" style="text-shadow: 0px 0px yellow;" data-value="0" title="Solo this video everywhere" onclick="requestInfocus(this);">
<i class="las la-user"></i>
<span data-translate="solo-video">Highlight guest</span>
</button>
@ -1228,7 +1243,7 @@
<span data-translate="hide-guest" >hide guest</span>
</button>
<button class="hidden" data-cluster="2" data-action-type="toggle-remote-speaker" title="Toggle the remote guest's speaker output" onclick="remoteSpeakerMute(this, event);">
<button class="hidden" data-cluster="2" data-action-type="toggle-remote-speaker"title="Toggle the remote guest's speaker output" onclick="remoteSpeakerMute(this, event);">
<i class="las la-volume-off"></i> <span data-translate="toggle-remote-speaker">Deafen Guest</span>
</button>
@ -1236,7 +1251,7 @@
<i class="las la-eye-slash"></i> <span data-translate="toggle-remote-display">Blind Guest</span>
</button>
<span class="hidden" data-cluster="2">
<span class="hidden" data-cluster="2" data-action-type="ordering">
<button style="width:34px;" data-action-type="order-down" title="Shift this Video Down in Order" onclick="changeOrder(-1,this.dataset.UUID);">
<span data-translate="order-down"><i class="las la-minus"></i></span>
</button>
@ -1259,6 +1274,8 @@
<span data-translate="change-params">URL Params</span>
</button>
<button class="hidden" data-cluster="2" data-action-type="recorder-local" title="Start Recording this remote stream to this local drive. *experimental*'" onclick="recordVideo(this, event)">
<i class="las la-circle"></i>
<span data-translate="record-local"> Record Local</span>
@ -1268,12 +1285,22 @@
<span data-translate="record-remote"> Record Remote</span>
</button>
<button class="hidden" data-cluster="2" data-action-type="open-file-share" data-value="0" title="Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link." onclick="requestFileUpload(this)">
<i class="las la-file-upload"></i>
<span data-translate="request-upload"> Request File</span>
</button>
<button class="hidden" data-cluster="2" data-action-type="create-timer" data-value="0" title="Set a countdown timer that this guest sees" onclick="directTimer(this, event);">
<i class="las la-clock"></i>
<span data-translate="create-timer">Create Timer</span>
</button>
<font class="tooltip hidden" data-cluster="2" style="height: 0; border: 0;">
<input data-action-type="volume" type="range" min="0" max="200" value="100" title="Remotely change the volume of this guest" oninput="remoteVolumeUI(this)" ondblclick="this.value=100;remoteVolume(this);remoteVolumeUI(this);" onchange="remoteVolume(this);" style="grid-column: 2; margin:5px; width: 93%; position: relative;top: 0.6em; background-color:#fff0;"/><span class="tooltiptext" style='float: right; overflow: auto; left: 40px; width: 2.5em; top: -13px; margin: 0; position:relative;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus,Code2000, Code2001, Code2002, Musica, serif, LastResort;' >100</span>
</font>
<span class="hidden" data-cluster="2">
<span class="hidden" data-cluster="2" data-action-type="change-quality">
<button style="width: 35.2px" data-action-type="change-quality1" title="Disable Video Preview" onclick="toggleQualityDirector(0, this.dataset.UUID, this);">
<span data-translate="change-to-low-quality">&nbsp;&nbsp;<i class="las la-video-slash"></i></span>
</button>
@ -1365,7 +1392,7 @@
<button data-action-type="solo-video" data-value="0" title="Solo this video everywhere" onclick="requestInfocus(this);">
<i class="las la-user"></i>
<span data-translate="solo-video">Highlight guest</span>
<span data-translate="solo-video">Highlight</span>
</button>
<button data-action-type="recorder-local" title="Start Recording this remote stream to this local drive. *experimental*'" onclick="recordLocalVideoToggle();">
@ -1424,14 +1451,14 @@
<br />
<div class="form-group multiselect" alt="tip: Hold CTRL (command) to select Multiple" title="tip: Hold CTRL (command) to select Multiple" style="padding: 10px; background-color:#f3f3f3;">
<div class="form-group multiselect" alt="tip: Hold CTRL (command) to select Multiple" title="tip: Hold CTRL (command) to select Multiple" >
<a id="multiselect-trigger3" class="form-control multiselect-trigger" >
<div id="audioTitle2" class="title">
<i class="las la-microphone-alt"></i><span data-translate="select-audio-source"> Audio Source(s) </span>
<i id="chevarrow2" class="chevron bottom" aria-hidden="true"></i>
</div>
</a>
<ul id="audioSource3" style="background-color:white;" class="multiselect-contents">
<ul id="audioSource3" class="multiselect-contents">
<li>
</li>
</ul>
@ -1454,8 +1481,11 @@
<option value="3" data-translate="blurred-background">Blurred background</option>
<option value="4" data-translate="digital-greenscreen">Digital greenscreen</option>
<option value="5" data-translate="virtual-background">Virtual background</option>
<option class="advanced" value="6" data-translate="face-mesh">Face mesh (slow load)</option>
<option class="advanced" value="anon" data-translate="anonymous-mask">Anonymous mask</option>
<option class="advanced" value="dog" data-translate="dog-face">Dog ears and nose</option>
</select>
<span data-warnSimdNotice="true" style='display:none; font-size: 140%; margin-left:10px; vertical-align: middle; cursor:pointer' title="Improve performance and quality with this tip" onclick="warnUser(`For improved performance, use Chrome v87 or newer with SIMD support enabled.<br />Enable SIMD here: <a href='chrome://flags/#enable-webassembly-simd' onclick='popupMessage(event);copyFunction(this)' target='_blank'>chrome://flags/#enable-webassembly-simd</a>`);">
<span data-warnSimdNotice="true" style='display:none; font-size: 140%; margin-left:10px; vertical-align: middle; cursor:pointer' title="Improve performance and quality with this tip" onclick="warnUser(`For improved performance, use Chrome v87 or newer with SIMD support enabled.<br />Enable SIMD here: <a href='chrome://flags/#enable-webassembly-simd' onclick='copyFunction(this,event)' target='_blank'>chrome://flags/#enable-webassembly-simd</a>`);">
<i class="las la-info-circle"></i>
</span>
<div id="selectImageTFLITE3" style="display:none;margin-top:10px;">
@ -1469,6 +1499,7 @@
</span>
<button id="shareScreenGear" style="width: 135px; padding:20px;text-align:center;" onclick="grabScreen()"><b>Share Screen</b><br /><i style="padding:5px; font-size:300%;" class="las la-desktop"></i></button>
<button id="pIpStartButton" style="width: 135px; background-color:#EFEFEF;padding:20px;text-align:center;display:none;"><b>Preview PiP VIdeo</b><br /><i style="padding:5px; font-size:300%;color:black;" class="las la-compress-arrows-alt"></i></button>
<br />
@ -1495,13 +1526,47 @@
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="Open">
<i class="las la-external-link"></i>
<span data-translate="open-in-new-tab">Open in new Tab</span>
<span data-translate="open-in-new-tab">Open in new tab</span>
</a>
</li>
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="Copy">
<i class="las la-paperclip"></i>
<span data-translate="copy-to-clipboard" >Copy to Clipboard</span>
<span data-translate="copy-to-clipboard" >Copy to clipboard</span>
</a>
</li>
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="Edit">
<i class="las la-pen"></i>
<span data-translate="edit-url" >Edit URL manually</span>
</a>
</li>
</ul>
</nav>
<nav id="context-menu-video" class="context-menu">
<ul class="context-menu__items">
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="Mirror">
<i class="las la-external-link"></i>
<span data-translate="mirror-video">Mirror</span>
</a>
</li>
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="Controls">
<i class="las la-external-link"></i>
<span data-translate="toggle-control-video">Toggle control bar</span>
</a>
</li>
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="PiP">
<i class="las la-external-link"></i>
<span data-translate="picture-in-picture">Picture-in-picture</span>
</a>
</li>
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="Cast">
<i class="las la-external-link"></i>
<span data-translate="chrome-cast">Cast..</span>
</a>
</li>
</ul>
@ -1527,7 +1592,10 @@
<label title="Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded." for="trbSettingInput">Change room video quality:</label>
<span style="margin-left: 6px;" id="trbSettingInputFeedback"></span>-kbps
<input id="trbSettingInput" type="range" min="0" max="4000" value="500" onchange="changeTRB(this);" oninput="getById('trbSettingInputFeedback').innerHTML = this.value;" style="width:100%;display:block;" />
<span style="margin: 20px 0 0 0;display:block" id='highlightDirectorSpan'>
<label for="highlightDirector">Highlight Director</label>
<input id="highlightDirector" style="width: 15px; height: 15px; margin:10px;" name="highlightDirector" data-value="0" data-action-type="solo-video" type="checkbox" onchange="requestInfocus(this);" />
</span>
</div>
</div>
@ -1641,12 +1709,13 @@
<li><a onclick="changeLg('nl');toggle(document.getElementById('languages'));" style="cursor: pointer;" data-tz="Europe/Amsterdam">Dutch</a></li>
<li><a onclick="changeLg('tr');toggle(document.getElementById('languages'));" style="cursor: pointer;" data-tz="Europe/Istanbul">Turkish</a></li>
<li><a onclick="changeLg('ja');toggle(document.getElementById('languages'));" style="cursor: pointer;" data-tz="Asia/Tokyo">Japanese</a></li>
<li><a onclick="changeLg('cn');toggle(document.getElementById('languages'));" style="cursor: pointer;" data-tz="Asia/China">Chinese (中文)</a></li>
<li><a onclick="changeLg('cs');toggle(document.getElementById('languages'));" style="cursor: pointer;" data-tz="Europe/Prague">Czech</a></li>
<li><a onclick="changeLg('uk');toggle(document.getElementById('languages'));" style="cursor: pointer;" data-tz="Europe/Ukraine">Ukrainian</a></li>
<li><a onclick="changeLg('pig');toggle(document.getElementById('languages'));" style="cursor: pointer;">Pig Latin</a></li>
</ul>
<br />
<a href="https://github.com/steveseguin/vdoninja/tree/master/translations" target="_blank" rel="noopener noreferrer" data-translate='add-more-here'>Add More Here!</a>
<a href="https://github.com/steveseguin/vdoninja/tree/master/translations" style='color:#333;font-style: italic;' target="_blank" rel="noopener noreferrer" data-translate='add-more-here'>Add More Here!</a>
<br />
</div>
<div id="calendar" class="popup-message" style="display: none; right: 0; padding-left:5px; bottom: 25px; position: absolute;">
@ -1669,10 +1738,13 @@
window.location = window.location.href.replace("www.obs.ninja","obs.ninja"); // the session.salt is domain specific; let's consolidate www as a result.
} else if (window.location.hostname.indexOf("www.vdo.ninja") == 0) {
window.location = window.location.href.replace("www.vdo.ninja","vdo.ninja"); // the session.salt is domain specific; let's consolidate www as a result.
} else if (("isSecureContext" in window) && (window.isSecureContext===false)){
console.error("This site must be run in a secure context; please ensure all links, iframes, and parent windows are using SSL");
}
var session = WebRTC.Media; // session is a required global variable if configuring manually. Run before loading main.js but after webrtc.js.
session.version = "18.3";
session.version = "19.1";
session.streamID = session.generateStreamID(); // randomly generates a streamID for this session. You can set your own programmatically if needed
session.defaultPassword = "someEncryptionKey123"; // Change this password if self-deploying for added security/privacy
@ -1749,11 +1821,11 @@
// session.title // "zzzz"
</script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/aes.js"></script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=12"></script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=66"></script>
<!--
// If you wish to change branding, blank offers a good clean start.
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
-->
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=239"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=251"></script>
</body>
</html>

425
it.json Normal file
View File

@ -0,0 +1,425 @@
{
"titles": {
"toggle-the-chat": "Attiva o disattiva la chat",
"mute-the-speaker": "Muta lo Speaker",
"mute-the-mic": "Disattiva il microfono",
"disable-the-camera": "Disattiva la Camera",
"settings": "Impostazioni",
"hangup-the-call": "Riaggancia la chiamata",
"show-help-info": "Mostra Info Helo",
"language-options": "Opzioni di lingua",
"tip-hold-ctrl-command-to-select-multiple": "tip: Tieni premut CTRL (command) per selezionare multipla ",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Ideale per il gioco a 1080p60 , se il tuo computer e upload lo supportano",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Migliore compressione e qualità video al costo di un maggiore carico di codifica della CPU ",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Disattiva gli effetti audio digitali e aumenta il bitrate audio ",
"the-guest-will-not-have-a-choice-over-audio-options": "L'ospite non avrà scelta sulle opzioni audio",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "L'ospite potrà solo selezionare la propria webcam come opzione",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Tieni premuto CTRL e la rotellina del mouse per ingrandire e ridurre in remoto i flussi video compatibili ",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Aggiungi una password per rendere il flusso inaccessibile a chi non ha la password",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Aggiungere l'ospite a una chat room di gruppo; verrà creato automaticamente se necessario.",
"customize-the-room-settings-for-this-guest": "Personalizza le impostazioni della stanza per questo ospite ",
"hold-ctrl-or-cmd-to-select-multiple-files": "Tieni premuto CTRL (or CMD) per selezionare più file",
"enter-an-https-url": "Inserisci un URL HTTPS ",
"lucy-g": "Lucy G",
"flaticon": "Flaticon",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"gregor-cresnar": "Gregor Cresnar",
"add-this-video-to-any-remote-scene-1-": "Aggiungi questo video a una scena in remoto '&scene=1'",
"forward-user-to-another-room-they-can-always-return-": "Inoltra l'utente a un'altra stanza. Possono sempre tornare.",
"start-recording-this-stream-experimental-views": "Inizia a registrare questo flusso. visualizzazioni *sperimentale*'",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Forza l'utente a disconnettersi. Possono sempre riconnettersi.",
"change-this-audio-s-volume-in-all-remote-scene-views": "Cambia il volume di questo audio in tutte le scene in remoto '&scene'",
"remotely-mute-this-audio-in-all-remote-scene-views": "Disattiva l'audio in remoto '&scene'",
"disable-video-preview": "Disattiva anteprima video",
"low-quality-preview": "Low-Quality Preview",
"high-quality-preview": "High-Quality Preview",
"send-direct-message": "Send Direct Message",
"advanced-settings-and-remote-control": "Advanced Settings and Remote Control",
"toggle-voice-chat-with-this-guest": "Toggle Voice Chat with this Guest",
"join-by-room-name-here": "Enter a room name to quick join",
"join-room": "Join room",
"share-a-screen-with-others": "Share a Screen with others",
"alert-the-host-you-want-to-speak": "Alert the host you want to speak",
"record-your-stream-to-disk": "Record your stream to disk",
"cancel-the-director-s-video-audio": "Cancel the Director's Video/Audio",
"submit-any-error-logs": "Submit any error logs",
"add-group-chat-to-obs": "Add Group Chat to OBS",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "For large group rooms, this option can reduce the load on remote guests substantially",
"which-video-codec-would-you-want-used-by-default-": "Which video codec would you want used by default?",
"you-ll-enter-as-the-room-s-director": "You'll enter as the room's director",
"add-your-camera-to-obs": "Add your Camera to OBS",
"remote-screenshare-into-obs": "Remote Screenshare into OBS",
"create-reusable-invite": "Create Reusable Invite",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "Encode the URL so that it's harder for a guest to modify the settings.",
"more-options": "More Options",
"youtube-video-demoing-how-to-do-this": "Youtube Video demoing how to do this",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Invite a guest or camera source to publish into the group room",
"if-enabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If enabled, the invited guest will not be able to see or hear anyone in the room.",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Use this link in the OBS Browser Source to capture the video or audio",
"if-enabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If enabled, you must manually add a video to a scene for it to appear.",
"disables-echo-cancellation-and-improves-audio-quality": "Disables Echo Cancellation and improves audio quality",
"audio-only-sources-are-visually-hidden-from-scenes": "Audio-only sources are visually hidden from scenes",
"guest-will-be-prompted-to-enter-a-display-name": "Guest will be prompted to enter a Display Name",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Display Names will be shown in the bottom-left corner of videos",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "Request 1080p60 from the Guest instead of 720p60, if possible",
"the-default-microphone-will-be-pre-selected-for-the-guest": "The default microphone will be pre-selected for the guest",
"the-default-camera-device-will-selected-automatically": "The default camera device will selected automatically",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "The guest won't have access to changing camera settings or screenshare",
"the-guest-will-not-see-their-own-self-preview-after-joining": "The guest will not see their own self-preview after joining",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Guests will have an option to poke the Director by pressing a button",
"add-an-audio-compressor-to-the-guest-s-microphone": "Add an audio compressor to the guest's microphone",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Add an Equalizer to the guest's microphone that the director can control",
"the-guest-can-only-see-the-director-s-video-if-provided": "The guest can only see the Director's video, if provided",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "The guest's microphone will be muted on joining. They can unmute themselves.",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Have the guest join muted, so only the director can Unmute the guest.",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Make the invite URL encoded, so parameters are harder to tinker with by guests",
"move-the-user-to-another-room-controlled-by-another-director": "Move the user to another room, controlled by another director",
"send-a-direct-message-to-this-user-": "Send a Direct Message to this user.",
"remotely-change-the-volume-of-this-guest": "Remotely change the volume of this guest",
"mute-this-guest-everywhere": "Mute this guest everywhere",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Start Recording this remote stream to this local drive. *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "The Remote Guest will record their local stream to their local drive. *experimental*",
"shift-this-video-down-in-order": "Shift this Video Down in Order",
"current-index-order-of-this-video": "Current Index Order of this Video",
"shift-this-video-up-in-order": "Shift this Video Up in Order",
"remote-audio-settings": "Remote Audio Settings",
"advanced-video-settings": "Advanced Video Settings",
"activate-or-reload-this-video-device-": "Activate or Reload this video device.",
"load-the-next-guest-in-queue": "Load the next guest in queue",
"create-a-secondary-stream": "Create a Secondary Stream",
"add-to-calendar": "Add to Calendar",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "The director will be visible in scenes, as if a performer themselves.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Useful if you want to perform and direct at the same time",
"start-streaming": "start streaming",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If disabled, the invited guest will not be able to see or hear anyone in the room.",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If disabled, you must manually add a video to a scene for it to appear.",
"guests-not-actively-speaking-will-be-hidden": "Guests not actively speaking will be hidden",
"increase-video-quality-that-guests-in-room-see-": "Increase video quality that guests in room see.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "The guest will not be asked for a video device on connection",
"toggle-solo-voice-chat": "Toggle Solo Voice Chat",
"add-to-scene-2": "Add to Scene 2",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"set-to-default-audio-channel": "Set to Default Audio Channel",
"set-to-audio-channel-1": "Set to Audio Channel 1",
"set-to-audio-channel-2": "Set to Audio Channel 2",
"set-to-audio-channel-3": "Set to Audio Channel 3",
"set-to-audio-channel-4": "Set to Audio Channel 4",
"set-to-audio-channel-5": "Set to Audio Channel 5",
"toggle-the-remote-guest-s-speaker-output": "Toggle the remote guest's speaker output",
"toggle-the-remote-guest-s-display-output": "Toggle the remote guest's display output",
"set-to-audio-channel-6": "Set to Audio Channel 6",
"set-to-audio-channel-7": "Set to Audio Channel 7",
"set-to-audio-channel-8": "Set to Audio Channel 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"solo-this-video-everywhere": "Solo this video everywhere",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"cannot-see-videos": "Cannot see videos",
"cannot-hear-others": "Cannot hear others",
"see-director-only": "See director only",
"show-mini-preview": "Show Mini preview",
"raise-hand-button": "Raise hand button",
"show-labels": "Show labels",
"transfer-to-a-new-room": "Transfer to a new Room",
"enable-custom-password": "Enable custom password",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"add-this-video-to-any-remote-scene-2-": "Add this Video to any remote '&scene=2'",
"add-to-scene-8": "Add to Scene 8",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees",
"enter-a-room-name-to-quick-join": "Enter a room name to quick join"
},
"innerHTML": {
"logo-header": "<font id=\"qos\" style=\"color: white;\">O</font>BS.Ninja ",
"copy-this-url": "Copia questo URL in un OBS \"Browser Source\"",
"you-are-in-the-control-center": "Sei nel pannello di controllo",
"joining-room": "Ti stai unendo alla stanza",
"add-group-chat": "Aggiungi chat di gruppo a OBS",
"rooms-allow-for": "Le stanze consentono una chat di gruppo semplificata e la gestione avanzata di più flussi contemporaneamente.",
"room-name": "Nome stanza",
"password-input-field": "Password",
"enter-the-rooms-control": "Entra nella gestione della stanza",
"show-tips": "Mosta alcuni suggerimenti..",
"added-notes": "\n<u><i>Aggiungi note:</i></u>\n<li>Chiunque può entrare in una stanza se conosce il nome, quindi mantienilo unico</li>\n<li>Invita solo persone fidate nella stanza.</li>\n<li>I dispositivi iOS condivideranno solo il loro audio con altri ospiti; questa è principalmente una limitazione hardware</li>\n<li>Opzione \"Registrazione\" è considerata opzioneale.</li>\n",
"back": "Indietro",
"add-your-camera": "Aggiungi camera ad OBS",
"ask-for-permissions": "Allow Access to Camera/Microphone",
"waiting-for-camera": "In attesa di caricamento",
"video-source": "Sorgente Video",
"max-resolution": "1080p (hi-def)",
"balanced": "720p (balanced)",
"smooth-cool": "360p (smooth)",
"select-audio-source": "Seleziona sorgente audio",
"no-audio": "No Audio",
"select-output-source": " Destinazione Output Audio : \n\t\t\t\t\t",
"remote-screenshare-obs": "Screenshare remoto dentro OBS",
"note-share-audio": "\n<b>note</b>: Do not forget to click \"Condividi audio\" in Chrome.<br>(Firefox non supporta la condivisione dell'audio..)",
"select-screen-to-share": "Seleziona lo schermo da condividere",
"audio-sources": "Sorgenti Audio",
"create-reusable-invite": "Crea un invito riutilizzabile",
"here-you-can-pre-generate": "Qui è possibile pre-generare un collegamento Sorgente del browser riutilizzabile e un collegamento di invito ospite correlato.",
"generate-invite-link": "Genera Link invito",
"advanced-paramaters": "Opzioni avanzate:",
"unlock-video-bitrate": "Sblocca Video Bitrate (20mbps)",
"force-vp9-video-codec": "Forza VP9 Video Codec (less artifacting)",
"enable-stereo-and-pro": "Abilita Stereo e Pro HD Audio",
"video-resolution": "Risoluzioni Video: ",
"hide-mic-selection": "Force Default Microphone",
"hide-screen-share": "Nascondi opzione Screenshare",
"allow-remote-control": "Controllo remoto camera zoom (android)",
"add-a-password-to-stream": " Aggiungi Password:",
"add-the-guest-to-a-room": "Aggiungi l'ospite a una stanza:",
"invite-group-chat-type": "In questa stanza ospite può fare:",
"can-see-and-hear": "Può vedere e ascoltare la chat di gruppo",
"can-hear-only": "Può solo ascoltare la chat di gruppo",
"cant-see-or-hear": "Impossibile ascoltare o vedere la chat di gruppo",
"share-local-video-file": "Stream Media File",
"share-website-iframe": "Share Website",
"run-a-speed-test": "Run a Speed Test",
"read-the-guides": "Browse the Guides",
"info-blob": "\n<h2>Cosa è OBS.Ninja</h2><br>\n<li>100% <b>free</b>; nessun download; nessuna raccolta di dati personali; nessun accesso</li>\n<li>Porta video dal tuo smartphone, computer o amici direttamente nel tuo flusso video OBS</li>\n<li>Utilizziamo una tecnologia di inoltro peer-to-peer all'avanguardia che offre privacy e latenza ultra bassa</li>\n<br>\n<li>Youtube video <i class=\"fa fa-youtube-play\" aria-hidden=\"true\"></i> <a href=\"https://www.youtube.com/watch?v=6R_sQKxFAhg\">Demoing è presente</a> </li>",
"add-to-scene": "Add to Scene",
"forward-to-room": "Transfer",
"record": "Registra",
"disconnect-guest": "Hangup",
"mute": "Muta",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Message",
"advanced-camera-settings": "<i class=\"las la-cog\"></i> Advanced",
"voice-chat": "<i class=\"las la-microphone\"></i> Voice Chat",
"open-in-new-tab": "Apri in una nuova Tab",
"copy-to-clipboard": "Copia negli appunti",
"click-for-quick-room-overview": "❔ Fare clic qui per una rapida panoramica e assistenza ",
"push-to-talk-enable": "🔊 Abilita Director's Push-to-Talk Mode",
"welcome-to-control-room": "Benvenuto. Questa è la sala di controllo per la chat di gruppo. Ci sono diverse cose per cui puoi usare questa stanza:<br><br>\t<li>Puoi ospitare una chat di gruppo con gli amici utilizzando una stanza. Condividi il link blu per invitare gli ospiti che si uniranno automaticamente alla chat.</li>\t<li>Una sala per gruppi può ospitare da 4 a 30 persone, a seconda di numerosi fattori, tra cui CPU e larghezza di banda disponibile di tutti gli ospiti nella stanza.</li>\t<li>Solo-views of each video are offered under videos as they load. These can be used within an OBS Browser Source.</li>\t<li>You can use the auto-mixing Group Scene, the green link, to auto arrange multiple videos for you in OBS.</li>\t<li>You can use this control room to record isolated video or audio streams, but it is an experimental feature still.</li>\t<li>Videos in the Director's room will be of low quality on purpose; to save bandwidth/CPU</li>\t<li>Guest's in the room will see each other's videos at a very limited quality to conserve bandwidth/CPU.</li>\t<li>OBS will see a guest's video in high-quality; the default video bitrate is 2500kbps.</li>\t<br>\tAs guests join, their videos will appear below. You can bring their video streams into OBS as solo-scenes or you can add them to the Group Scene.\t<br>The Group Scene auto-mixes videos that have been added to the group scene. Please note that the Auto-Mixer requires guests be manually added to it for them to appear in it; they are not added automatically.<br><br>Apple mobile devices, such as iPhones and iPads, do not fully support Video Group Chat. This is a hardware constraint.<br><br>\tFor advanced options and parameters, <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">see the Wiki.</a>",
"more-than-four-can-join": "These four guest slots are just for demonstration. More than four guests can actually join a room.",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tBenvenuto in OBS.Ninja! Da qui puoi inviare messaggi di testo direttamente ai peer connessi.\n\t\t\t\t",
"names-and-labels-coming-soon": "\n\t\t\t\t\tI nomi che identificano i peer connessi saranno una funzionalità in una prossima versione..\n\t\t\t\t",
"send-chat": "Invia",
"available-languages": "Lingue Disponibili:",
"add-more-here": "Aggiungi altro qui!",
"waiting-for-camera-to-load": "waiting-for-camera-to-load",
"start": "START",
"share-your-mic": "Condividi il tuo microfono",
"share-your-camera": "Condividi Camera",
"share-your-screen": "Condividi il tuo schermo",
"join-room-with-mic": "Entra nella stanza con il Microfono",
"share-screen-with-room": "Condivisione dello schermo con la stanza",
"join-room-with-camera": "Entra nella stanza con la Camera ",
"click-start-to-join": "Fare clic su Start per partecipare",
"guests-only-see-director": "Guests can only see the Director's Video",
"default-codec-select": "Preferred Video Codec: ",
"obfuscate_url": "Obfuscate the Invite URL",
"hide-the-links": " LINKS (GUEST INVITES &amp; SCENES)",
"invite-users-to-join": "Guests can use the link to join the group room",
"this-is-obs-browser-source-link": "Use in OBS or other studio software to capture the group video mix",
"mute-scene": "mute in scene",
"mute-guest": "mute guest",
"record-local": " Record Local",
"record-remote": " Record Remote",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Audio Settings",
"scenes-can-see-director": "Director will also be a performer",
"select-digital-effect": " Digital Video Effects: ",
"add-a-password": " Add a Password:",
"hide-guest": "hide guest",
"toggle-remote-speaker": "Deafen Guest",
"toggle-remote-display": "Blind Guest",
"force-keyframe": "Rainbow Puke",
"change-url": "Change URL",
"change-params": "URL Params",
"solo-video": "Highlight guest",
"stats-remote": " Scene Stats",
"apply-new-guest-settings": "Apply settings",
"cancel": "Cancel",
"add-to-calendar": "Add details to your Calendar:",
"no-effects-applied": "No effects applied",
"blurred-background": "Blurred background",
"digital-greenscreen": "Digital greenscreen",
"virtual-background": "Virtual background",
"use-chrome-instead": "Consider using a Chromium-based browser instead.<br>\n \t\t\t\t\t\tSafari is more prone to having audio issues",
"select-the-video-files-to-share": "SELECT THE VIDEO FILES TO SHARE",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"click-here-for-help": "Click Here for a quick overview and help",
"guests-hear-others": "Guests hear others",
"capture-a-group-scene": "CAPTURE A GROUP SCENE",
"auto-add-guests": "Auto-add guests",
"pro-audio-mode": "Pro-audio mode",
"hide-audio-only-sources": "Hide audio-only sources",
"ask-for-display-name": "Ask for display name",
"show-display-names": "Show display names",
"show-active-speaker": "Show active speakers",
"auto-select-microphone": "Auto-select default microphone",
"auto-select-camera": "Auto-select default camera",
"hide-setting-buttons": "Hide settings button",
"mini-self-preview": "Mini self-preview",
"virtual-backgrounds": "Virtual backgrounds",
"powerful-computers-only": "Only use with powerful computers and small groups!!",
"guests-see-HD-video": "Guests see HD video",
"no-self-preview": "Disable self-preview",
"raise-hand-button": "Display 'raise-hand' button",
"enable-compressor": "Enable audio compressor",
"enable-equalizer": "Enable equalizer as option",
"low-cpu=broadcast-codec": "Low-CPU broadcast codec",
"only-see-director-feed": "Only see the director's feed",
"mute-microphone-by-default": "Mute microphone by default",
"guest-joins-with-no-camera": "Guest joins with no camera",
"unmute-by-director-only": "Unmute by director only",
"obfuscate-link": "Obfuscate link and parameters",
"this-can-reduce-packet-loss": "This can reduce video corruption caused by packet loss",
"use-h264-codec": "Use H264 codec",
"show-active-speakers": "Show active speakers",
"force-mono-audio": "Force mono audio",
"learn-more-about-params": "Learn more about URL parameters at ",
"More-scene-options": "More scene options",
"additional-controls": "Additional controls",
"select-local-image": "Select Local Image",
"close-settings": "Close Settings",
"advanced": "Advanced ",
"invisible-guests": "Not Visible",
"add-to-google-calendar": "Add to Google Calendar",
"add-to-outlook-calendar": "Add to Outlook Calendar",
"add-to-yahoo-calendar": "Add to Yahoo Calendar",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"fade-videos-in": "Fade videos in",
"show-guest-tips": "Show guest setup tips",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"add-to-scene2": "add to scene 2",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast..",
"join-room": "Join room",
"waiting-for-mic-to-load": "Waiting for mic to load"
},
"placeholders": {
"join-by-room-name-here": "Join by Room Name here",
"enter-a-room-name-here": "Enter a Room Name here",
"optional-room-password-here": "Optional room password here",
"give-this-media-source-a-name-optional-": "Give this media source a name (optional)",
"add-an-optional-password": "Add an optional password",
"enter-room-name-here": "Enter Room name here",
"enter-chat-message-to-send-here": "Enter chat message to send here",
"optional": "optional",
"enter-the-room-name-here": "Enter the room name here",
"enter-the-room-password-here": "Enter the room password here",
"enter-your-message-here": "Enter your message here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "Enter a new Display Name for this stream",
"submit-error-report": "Press OK to submit any error logs to VDO.Ninja. Error logs may contain private information.",
"director-redirect-1": "The director wishes to redirect you to the URL: ",
"director-redirect-2": "\n\nPress OK to be redirected.",
"add-a-label": "Add a label",
"audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. You can try claiming the room after the first director leaves.</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.",
"director": "Director",
"unknown-user": "Unknown User",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?",
"load-previous-session": "Would you like to load your previous session's settings?",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.",
"enter-display-name": "Please enter your display name:",
"enter-new-display-name": "Enter a new Display Name for this stream",
"what-bitrate": "What bitrate would you like to record at? (kbps)",
"enter-website": "Enter a website URL to share",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.",
"share-with-trusted": "Share only with those you trust",
"pass-recommended": "A password is recommended",
"insecure-room-name": "Insecure room name.",
"allowed-chars": "Allowed chars",
"transfer": "transfer",
"armed": "armed",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)",
"change-url": "change URL",
"mute-in-scene": "mute in scene",
"unmute-guest": "un-mute guest",
"undeafen": "un-deafen",
"deafen": "deafen guest",
"unblind": "un-blind",
"blind": "blind guest",
"unmute": "un-mute",
"mute-guest": "mute guest",
"unhide": "unhide guest",
"hide-guest": "hide guest",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

425
ja.json Normal file
View File

@ -0,0 +1,425 @@
{
"titles": {
"toggle-the-chat": "Toggle the Chat",
"mute-the-speaker": "Mute the Speaker",
"mute-the-mic": "Mute the Mic",
"disable-the-camera": "Disable the Camera",
"settings": "Settings",
"hangup-the-call": "Hangup the Call",
"show-help-info": "Show Help Info",
"language-options": "Language Options",
"tip-hold-ctrl-command-to-select-multiple": "tip: Hold CTRL (command) to select Multiple",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Ideal for 1080p60 gaming, if your computer and upload are up for it",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Better video compression and quality at the cost of increased CPU encoding load",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Disable digital audio-effects and increase audio bitrate",
"the-guest-will-not-have-a-choice-over-audio-options": "The guest will not have a choice over audio-options",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "The guest will only be able to select their webcam as an option",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Hold CTRL and the mouse wheel to zoom in and out remotely of compatible video streams",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Add a password to make the stream inaccessible to those without the password",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Add the guest to a group-chat room; it will be created automatically if needed.",
"customize-the-room-settings-for-this-guest": "Customize the room settings for this guest",
"hold-ctrl-or-cmd-to-select-multiple-files": "Hold CTRL (or CMD) to select multiple files",
"enter-an-https-url": "Enter an HTTPS URL",
"lucy-g": "Lucy G",
"flaticon": "Flaticon",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"gregor-cresnar": "Gregor Cresnar",
"add-this-video-to-any-remote-scene-1-": "Add this Video to any remote '&scene=1'",
"forward-user-to-another-room-they-can-always-return-": "Forward user to another room. They can always return.",
"start-recording-this-stream-experimental-views": "Start Recording this stream. *experimental*' views",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Force the user to Disconnect. They can always reconnect.",
"change-this-audio-s-volume-in-all-remote-scene-views": "Change this Audio's volume in all remote '&scene' views",
"remotely-mute-this-audio-in-all-remote-scene-views": "Remotely Mute this Audio in all remote '&scene' views",
"disable-video-preview": "Disable Video Preview",
"low-quality-preview": "Low-Quality Preview",
"high-quality-preview": "High-Quality Preview",
"send-direct-message": "Send Direct Message",
"advanced-settings-and-remote-control": "Advanced Settings and Remote Control",
"toggle-voice-chat-with-this-guest": "Toggle Voice Chat with this Guest",
"join-by-room-name-here": "Enter a room name to quick join",
"join-room": "Join room",
"share-a-screen-with-others": "Share a Screen with others",
"alert-the-host-you-want-to-speak": "Alert the host you want to speak",
"record-your-stream-to-disk": "Record your stream to disk",
"cancel-the-director-s-video-audio": "Cancel the Director's Video/Audio",
"submit-any-error-logs": "Submit any error logs",
"add-group-chat-to-obs": "Add Group Chat to OBS",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "For large group rooms, this option can reduce the load on remote guests substantially",
"which-video-codec-would-you-want-used-by-default-": "Which video codec would you want used by default?",
"you-ll-enter-as-the-room-s-director": "You'll enter as the room's director",
"add-your-camera-to-obs": "Add your Camera to OBS",
"remote-screenshare-into-obs": "Remote Screenshare into OBS",
"create-reusable-invite": "Create Reusable Invite",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "Encode the URL so that it's harder for a guest to modify the settings.",
"more-options": "More Options",
"youtube-video-demoing-how-to-do-this": "Youtube Video demoing how to do this",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Invite a guest or camera source to publish into the group room",
"if-enabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If enabled, the invited guest will not be able to see or hear anyone in the room.",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Use this link in the OBS Browser Source to capture the video or audio",
"if-enabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If enabled, you must manually add a video to a scene for it to appear.",
"disables-echo-cancellation-and-improves-audio-quality": "Disables Echo Cancellation and improves audio quality",
"audio-only-sources-are-visually-hidden-from-scenes": "Audio-only sources are visually hidden from scenes",
"guest-will-be-prompted-to-enter-a-display-name": "Guest will be prompted to enter a Display Name",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Display Names will be shown in the bottom-left corner of videos",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "Request 1080p60 from the Guest instead of 720p60, if possible",
"the-default-microphone-will-be-pre-selected-for-the-guest": "The default microphone will be pre-selected for the guest",
"the-default-camera-device-will-selected-automatically": "The default camera device will selected automatically",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "The guest won't have access to changing camera settings or screenshare",
"the-guest-will-not-see-their-own-self-preview-after-joining": "The guest will not see their own self-preview after joining",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Guests will have an option to poke the Director by pressing a button",
"add-an-audio-compressor-to-the-guest-s-microphone": "Add an audio compressor to the guest's microphone",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Add an Equalizer to the guest's microphone that the director can control",
"the-guest-can-only-see-the-director-s-video-if-provided": "The guest can only see the Director's video, if provided",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "The guest's microphone will be muted on joining. They can unmute themselves.",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Have the guest join muted, so only the director can Unmute the guest.",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Make the invite URL encoded, so parameters are harder to tinker with by guests",
"move-the-user-to-another-room-controlled-by-another-director": "Move the user to another room, controlled by another director",
"send-a-direct-message-to-this-user-": "Send a Direct Message to this user.",
"remotely-change-the-volume-of-this-guest": "Remotely change the volume of this guest",
"mute-this-guest-everywhere": "Mute this guest everywhere",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Start Recording this remote stream to this local drive. *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "The Remote Guest will record their local stream to their local drive. *experimental*",
"shift-this-video-down-in-order": "Shift this Video Down in Order",
"current-index-order-of-this-video": "Current Index Order of this Video",
"shift-this-video-up-in-order": "Shift this Video Up in Order",
"remote-audio-settings": "Remote Audio Settings",
"advanced-video-settings": "Advanced Video Settings",
"activate-or-reload-this-video-device-": "Activate or Reload this video device.",
"load-the-next-guest-in-queue": "Load the next guest in queue",
"create-a-secondary-stream": "Create a Secondary Stream",
"add-to-calendar": "Add to Calendar",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "The director will be visible in scenes, as if a performer themselves.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Useful if you want to perform and direct at the same time",
"start-streaming": "start streaming",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If disabled, the invited guest will not be able to see or hear anyone in the room.",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If disabled, you must manually add a video to a scene for it to appear.",
"guests-not-actively-speaking-will-be-hidden": "Guests not actively speaking will be hidden",
"increase-video-quality-that-guests-in-room-see-": "Increase video quality that guests in room see.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "The guest will not be asked for a video device on connection",
"toggle-solo-voice-chat": "Toggle Solo Voice Chat",
"add-to-scene-2": "Add to Scene 2",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"set-to-default-audio-channel": "Set to Default Audio Channel",
"set-to-audio-channel-1": "Set to Audio Channel 1",
"set-to-audio-channel-2": "Set to Audio Channel 2",
"set-to-audio-channel-3": "Set to Audio Channel 3",
"set-to-audio-channel-4": "Set to Audio Channel 4",
"set-to-audio-channel-5": "Set to Audio Channel 5",
"toggle-the-remote-guest-s-speaker-output": "Toggle the remote guest's speaker output",
"toggle-the-remote-guest-s-display-output": "Toggle the remote guest's display output",
"set-to-audio-channel-6": "Set to Audio Channel 6",
"set-to-audio-channel-7": "Set to Audio Channel 7",
"set-to-audio-channel-8": "Set to Audio Channel 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"solo-this-video-everywhere": "Solo this video everywhere",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"cannot-see-videos": "Cannot see videos",
"cannot-hear-others": "Cannot hear others",
"see-director-only": "See director only",
"show-mini-preview": "Show Mini preview",
"raise-hand-button": "Raise hand button",
"show-labels": "Show labels",
"transfer-to-a-new-room": "Transfer to a new Room",
"enable-custom-password": "Enable custom password",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"add-this-video-to-any-remote-scene-2-": "Add this Video to any remote '&scene=2'",
"add-to-scene-8": "Add to Scene 8",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees",
"enter-a-room-name-to-quick-join": "Enter a room name to quick join"
},
"innerHTML": {
"logo-header": "<font id=\"qos\" style=\"color: white;\">O</font>BS.Ninja ",
"copy-this-url": "このURLをOBSの「ブラウザソース」に追加",
"you-are-in-the-control-center": "ルーム管理センター",
"joining-room": "ルームに参加しています",
"add-group-chat": "OBSにグループミーティングを追加",
"rooms-allow-for": "ルームを利用すると、グループミーティングや複数ストリームを、一つの画面で管理できます。",
"room-name": "ルーム名",
"password-input-field": "Password",
"enter-the-rooms-control": "ルーム管理センターに入る",
"show-tips": "ヒントを表示",
"added-notes": "<i><u>追加情報:<u></u></u></i><li><u><u>ルーム名を知っている人は誰でもルームに入れるため、ユニークなルーム名にして下さい。</u></u></li><li><u><u>ハードウェア性能にもよりますが、パフォーマンス上の理由から、4人以上のルーム利用はおすすめできません。</u></u></li><li><u><u>iOSデバイスでは、2人以下のグループサイズに制限されます。これはハードウェアによる制限です。</u></u></li><li><u><u>\"Recording\" オプションは実験的な新機能です。</u></u></li><li><u><u>ビデオフィードを「グループシーン」に表示するには、そこに「追加」する必要があります。</u></u></li><li><u><u>ゲストのビューに、新しい「強化されたフルスクリーン」ボタンが追加されました。</u></u></li>",
"back": "戻る",
"add-your-camera": "OBSに自分のカメラを追加",
"ask-for-permissions": "Allow Access to Camera/Microphone",
"waiting-for-camera": "カメラがロードされるのを待っています...",
"video-source": "映像ソース",
"max-resolution": "最大解像度",
"balanced": "バランス",
"smooth-cool": "スムーズ&クール",
"select-audio-source": "音声ソースを選択",
"no-audio": "音声なし",
"select-output-source": "音声出力先:",
"remote-screenshare-obs": "OBSに画面共有を追加",
"note-share-audio": "<strong>注意</strong>: Chromeの「音声の共有」を必ずクリックして下さい。<br>(Firefox は音声の共有をサポートしていません)",
"select-screen-to-share": "共有する画面を選択",
"audio-sources": "音声ソース",
"create-reusable-invite": "再利用可能な招待リンクを作成",
"here-you-can-pre-generate": "再利用可能なブラウザソースリンクと、関連するゲスト招待リンクを、事前に作成できます。",
"generate-invite-link": "招待リンクを作成",
"advanced-paramaters": "高度なパラメータ",
"unlock-video-bitrate": "ビデオビットレートをアンロック (20mbps)",
"force-vp9-video-codec": "VP9ビデオコーデックの使用を強制 (less artifacting)",
"enable-stereo-and-pro": "ステレオ・プロHDオーディオを有効にする",
"video-resolution": "ビデオ解像度: ",
"hide-mic-selection": "Force Default Microphone",
"hide-screen-share": "画面共有オプションを非表示",
"allow-remote-control": "カメラズームのリモートコントロール (android)",
"add-a-password-to-stream": "パスワードを追加:",
"add-the-guest-to-a-room": "ゲストをルームに追加:",
"invite-group-chat-type": "このルームのゲストは、次のことができる:",
"can-see-and-hear": "グループミーティングの映像・音声を視聴できる",
"can-hear-only": "グループミーティングの音声のみ聞ける",
"cant-see-or-hear": "グループミーティングの映像・音声を視聴できない",
"share-local-video-file": "Stream Media File",
"share-website-iframe": "Share Website",
"run-a-speed-test": "Run a Speed Test",
"read-the-guides": "Browse the Guides",
"info-blob": "<h2>OBS.Ninja とは?</h2><br><li>超低遅延でプライバシーが保護された、ビデオストリームサービスです。</li><li>ライブ配信でゲストとの対話を配信したり、少人数のグループミーティングにも利用できます。</li><li>100% <strong>無料</strong>、ダウンロード不要、サインイン不要、個人情報を一切収集しません。</li><li>あなたや友人のスマートフォン、タブレット、PCなどから、直接OBSビデオストリームに映像を取り込めます。</li><li>プライバシーと超低遅延を実現するために、最先端のピアツーピア転送技術を使用しています。</li><br><li>YouTube: <i class=\"fa fa-youtube-play\" aria-hidden=\"true\"></i> <a href=\"https://www.youtube.com/watch?v=6R_sQKxFAhg\">デモ動画を見る</a> </li>",
"add-to-scene": "Add to Scene",
"forward-to-room": "Transfer",
"record": "録画",
"disconnect-guest": "Hangup",
"mute": "ミュート",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Message",
"advanced-camera-settings": "<i class=\"las la-cog\"></i> Advanced",
"voice-chat": "<i class=\"las la-microphone\"></i> Voice Chat",
"open-in-new-tab": "新しいタブで開く",
"copy-to-clipboard": "クリップボードにコピー",
"click-for-quick-room-overview": "❔ Click Here for a quick overview and help",
"push-to-talk-enable": "🔊 Enable Director's Push-to-Talk Mode",
"welcome-to-control-room": "Welcome. This is the control-room for the group-chat. There are different things you can use this room for:<br><br>\t<li>You can host a group chat with friends using a room. Share the blue link to invite guests who will join the chat automatically.</li>\t<li>A group room can handle around 4 to 30 guests, depending on numerous factors, including CPU and available bandwidth of all guests in the room.</li>\t<li>Solo-views of each video are offered under videos as they load. These can be used within an OBS Browser Source.</li>\t<li>You can use the auto-mixing Group Scene, the green link, to auto arrange multiple videos for you in OBS.</li>\t<li>You can use this control room to record isolated video or audio streams, but it is an experimental feature still.</li>\t<li>Videos in the Director's room will be of low quality on purpose; to save bandwidth/CPU</li>\t<li>Guest's in the room will see each other's videos at a very limited quality to conserve bandwidth/CPU.</li>\t<li>OBS will see a guest's video in high-quality; the default video bitrate is 2500kbps.</li>\t<br>\tAs guests join, their videos will appear below. You can bring their video streams into OBS as solo-scenes or you can add them to the Group Scene.\t<br>The Group Scene auto-mixes videos that have been added to the group scene. Please note that the Auto-Mixer requires guests be manually added to it for them to appear in it; they are not added automatically.<br><br>Apple mobile devices, such as iPhones and iPads, do not fully support Video Group Chat. This is a hardware constraint.<br><br>\tFor advanced options and parameters, <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">see the Wiki.</a>",
"more-than-four-can-join": "These four guest slots are just for demonstration. More than four guests can actually join a room.",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tWelcome to OBS.Ninja! You can send text messages directly to connected peers from here.\n\t\t\t\t",
"names-and-labels-coming-soon": "\n\t\t\t\t\tNames identifying connected peers will be a feature in an upcoming release.\n\t\t\t\t",
"send-chat": "Send",
"available-languages": "Available Languages:",
"add-more-here": "Add More Here!",
"waiting-for-camera-to-load": "waiting-for-camera-to-load",
"start": "START",
"share-your-mic": "Share your microphone",
"share-your-camera": "Share your Camera",
"share-your-screen": "Share your Screen",
"join-room-with-mic": "Join room with Microphone",
"share-screen-with-room": "Share-screen with Room",
"join-room-with-camera": "Join room with Camera",
"click-start-to-join": "Click Start to Join",
"guests-only-see-director": "Guests can only see the Director's Video",
"default-codec-select": "Preferred Video Codec: ",
"obfuscate_url": "Obfuscate the Invite URL",
"hide-the-links": " LINKS (GUEST INVITES &amp; SCENES)",
"invite-users-to-join": "Guests can use the link to join the group room",
"this-is-obs-browser-source-link": "Use in OBS or other studio software to capture the group video mix",
"mute-scene": "mute in scene",
"mute-guest": "mute guest",
"record-local": " Record Local",
"record-remote": " Record Remote",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Audio Settings",
"scenes-can-see-director": "Director will also be a performer",
"select-digital-effect": " Digital Video Effects: ",
"add-a-password": " Add a Password:",
"hide-guest": "hide guest",
"toggle-remote-speaker": "Deafen Guest",
"toggle-remote-display": "Blind Guest",
"force-keyframe": "Rainbow Puke",
"change-url": "Change URL",
"change-params": "URL Params",
"solo-video": "Highlight guest",
"stats-remote": " Scene Stats",
"apply-new-guest-settings": "Apply settings",
"cancel": "Cancel",
"add-to-calendar": "Add details to your Calendar:",
"no-effects-applied": "No effects applied",
"blurred-background": "Blurred background",
"digital-greenscreen": "Digital greenscreen",
"virtual-background": "Virtual background",
"use-chrome-instead": "Consider using a Chromium-based browser instead.<br>\n \t\t\t\t\t\tSafari is more prone to having audio issues",
"select-the-video-files-to-share": "SELECT THE VIDEO FILES TO SHARE",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"click-here-for-help": "Click Here for a quick overview and help",
"guests-hear-others": "Guests hear others",
"capture-a-group-scene": "CAPTURE A GROUP SCENE",
"auto-add-guests": "Auto-add guests",
"pro-audio-mode": "Pro-audio mode",
"hide-audio-only-sources": "Hide audio-only sources",
"ask-for-display-name": "Ask for display name",
"show-display-names": "Show display names",
"show-active-speaker": "Show active speakers",
"auto-select-microphone": "Auto-select default microphone",
"auto-select-camera": "Auto-select default camera",
"hide-setting-buttons": "Hide settings button",
"mini-self-preview": "Mini self-preview",
"virtual-backgrounds": "Virtual backgrounds",
"powerful-computers-only": "Only use with powerful computers and small groups!!",
"guests-see-HD-video": "Guests see HD video",
"no-self-preview": "Disable self-preview",
"raise-hand-button": "Display 'raise-hand' button",
"enable-compressor": "Enable audio compressor",
"enable-equalizer": "Enable equalizer as option",
"low-cpu=broadcast-codec": "Low-CPU broadcast codec",
"only-see-director-feed": "Only see the director's feed",
"mute-microphone-by-default": "Mute microphone by default",
"guest-joins-with-no-camera": "Guest joins with no camera",
"unmute-by-director-only": "Unmute by director only",
"obfuscate-link": "Obfuscate link and parameters",
"this-can-reduce-packet-loss": "This can reduce video corruption caused by packet loss",
"use-h264-codec": "Use H264 codec",
"show-active-speakers": "Show active speakers",
"force-mono-audio": "Force mono audio",
"learn-more-about-params": "Learn more about URL parameters at ",
"More-scene-options": "More scene options",
"additional-controls": "Additional controls",
"select-local-image": "Select Local Image",
"close-settings": "Close Settings",
"advanced": "Advanced ",
"invisible-guests": "Not Visible",
"add-to-google-calendar": "Add to Google Calendar",
"add-to-outlook-calendar": "Add to Outlook Calendar",
"add-to-yahoo-calendar": "Add to Yahoo Calendar",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"fade-videos-in": "Fade videos in",
"show-guest-tips": "Show guest setup tips",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"add-to-scene2": "add to scene 2",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast..",
"join-room": "Join room",
"waiting-for-mic-to-load": "Waiting for mic to load"
},
"placeholders": {
"join-by-room-name-here": "Join by Room Name here",
"enter-a-room-name-here": "Enter a Room Name here",
"optional-room-password-here": "Optional room password here",
"give-this-media-source-a-name-optional-": "Give this media source a name (optional)",
"add-an-optional-password": "Add an optional password",
"enter-room-name-here": "Enter Room name here",
"enter-chat-message-to-send-here": "Enter chat message to send here",
"optional": "optional",
"enter-the-room-name-here": "Enter the room name here",
"enter-the-room-password-here": "Enter the room password here",
"enter-your-message-here": "Enter your message here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "Enter a new Display Name for this stream",
"submit-error-report": "Press OK to submit any error logs to VDO.Ninja. Error logs may contain private information.",
"director-redirect-1": "The director wishes to redirect you to the URL: ",
"director-redirect-2": "\n\nPress OK to be redirected.",
"add-a-label": "Add a label",
"audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. You can try claiming the room after the first director leaves.</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.",
"director": "Director",
"unknown-user": "Unknown User",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?",
"load-previous-session": "Would you like to load your previous session's settings?",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.",
"enter-display-name": "Please enter your display name:",
"enter-new-display-name": "Enter a new Display Name for this stream",
"what-bitrate": "What bitrate would you like to record at? (kbps)",
"enter-website": "Enter a website URL to share",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.",
"share-with-trusted": "Share only with those you trust",
"pass-recommended": "A password is recommended",
"insecure-room-name": "Insecure room name.",
"allowed-chars": "Allowed chars",
"transfer": "transfer",
"armed": "armed",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)",
"change-url": "change URL",
"mute-in-scene": "mute in scene",
"unmute-guest": "un-mute guest",
"undeafen": "un-deafen",
"deafen": "deafen guest",
"unblind": "un-blind",
"blind": "blind guest",
"unmute": "un-mute",
"mute-guest": "mute guest",
"unhide": "unhide guest",
"hide-guest": "hide guest",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

6471
lib.js

File diff suppressed because it is too large Load Diff

534
main.css
View File

@ -11,6 +11,8 @@
--fit-style: contain;
--fadein-speed: 0;
--video-margin: 0px;
--video-rounded: 0px;
--color-mode: light;
}
* {
@ -27,6 +29,19 @@ table {
margin:10px;
}
.hideDropMenu{
user-select: none;
grid-column: 1;
width:100%;
margin:5px 0;
font-size:80%;
cursor: pointer;
}
#audioSource3{
background-color: #FFF;
}
#bigPlayButton {
margin:0 auto;
background-color: #0000;
@ -168,6 +183,10 @@ button.grey {
background-color: #673100 !important;
}
.directorBlue {
background-color: #5c7785 !important;
}
button.red {
-webkit-app-region: no-drag;
padding: 10px;
@ -210,7 +229,6 @@ button.white:active {
color: white;
border: 1px solid black;
}
#header {
width: 100%;
padding: 1px;
@ -223,8 +241,9 @@ button.white:active {
color: white;
text-align: right;
margin-right: 10px;
pointer-events: none;
cursor: help;
float: right;
font-size: 90%;
}
#head6 {
display: inline-block;
@ -245,7 +264,26 @@ button.white:active {
pointer-events: none;
font-weight: 700;
}
#overlayClockContainer{
margin: 0 auto;
background-color: #0000;
color: white;
font-family: Cousine, monospace;
font-size: calc(6vh + 6vw / 2);
letter-spacing: 0.0em;
text-shadow: 0.05em 0.05em 0px rgb(0 0 0);
width: 100%;
z-index: 6;
vertical-align: top;
text-align: center;
position: fixed;
overflow-wrap: anywhere;
pointer-events: none;
}
#overlayClock{
padding:2px 20px;
background-color: #0009;
}
#overlayMsgs{
margin:0 auto;
background-color: #0000;
@ -281,6 +319,7 @@ button.white:active {
right: 0;
z-index: -1;
font-size: 80%;
margin-right:100px;
}
.credits>a {
@ -402,9 +441,9 @@ hr {
width: 100%;
height: 100%;
border: 0;
padding: 0;
overflow: hidden;
margin: var(--video-margin);
padding: var(--video-margin);
border-radius: var(--video-rounded);
}
#gridlayout {
@ -467,7 +506,11 @@ hr {
background: #999999;
width: 90%;
}
#activeShares>div{
font-weight: normal;
font-size: 12px;
margin: 10px 0 0 0;
}
.puslate {
border-radius: 50%;
box-shadow: 0 0 0 0 rgba(14, 19, 26, 1);
@ -599,22 +642,16 @@ button.glyphicon-button.active.focus {
@media only screen and (max-height: 400px){
#obsState {
transform: scale(0.5);
display:none!important;
opacity:0;
}
}
@media only screen and (max-height: 300px){
#obsState {
transform: scale(0.4);
display:none!important;
opacity:0;
}
}
@media only screen and (max-height: 200px){
#obsState {
transform: scale(0.3);
display:none!important;
opacity:0;
}
}
@ -646,6 +683,9 @@ button.btnArmTransferRoom.selected{
#controlButtons {
height:54px;
}
#copythisurl {
font-size:80%;
}
}
@media only screen and (max-height: 500px){
#subControlButtons {
@ -697,6 +737,7 @@ button.btnArmTransferRoom.selected{
#head2 {
display:none !important;
}
}
@media only screen and (max-height: 240px){
#gridlayout>#container.vidcon {
@ -830,11 +871,6 @@ body {
transition: opacity .1s linear;
}
.hidden {
visibility: hidden;
opacity: 0;
}
.previewWebcam {
max-width: 640px;
max-width: 83vw;
@ -916,6 +952,10 @@ body {
background-color:#ddeeff;
}
.green {
background-color:#474!important;
}
/*https://css-tricks.com/styling-cross-browser-compatible-range-inputs-css/*/
input[type=range] {
-webkit-appearance: none;
@ -1192,7 +1232,7 @@ input[type=range]:focus::-ms-fill-upper {
#audioSourceScreenshare {
display:block;
height: 60px;
min-width: 290px;
width: 100%;
overflow: auto;
padding: 5px;
resize: both;
@ -1204,6 +1244,17 @@ p#audioScreenShare1 {
background: #f3f3f3;
padding: 4px 10px 10px 10px;
text-align: left;
min-width:350px;
}
#headphonesDiv2{
background-color: #f3f3f3;
min-width: 350px;
display: none;
padding: 4px 10px 10px 10px;
border: 1px solid #ccc;
vertical-align: middle;
margin: 10px 0;
text-align: left;
}
#audioScreenShare1 > i {
@ -1379,27 +1430,27 @@ img {
background-repeat: no-repeat;
background-size: 80px;
background-position: 50% 65%;
background-image: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMjkgMTI5IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMjkgMTI5IiB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiPgogIDxnPgogICAgPGc+CiAgICAgIDxwYXRoIGQ9Im0xMC41LDU4LjloNDQuM2MyLjMsMCA0LjEtMS44IDQuMS00LjF2LTQ0LjNjMC0yLjMtMS44LTQuMS00LjEtNC4xaC00NC4zYy0yLjMsMC00LjEsMS44LTQuMSw0LjF2NDQuM2MwLDIuMiAxLjksNC4xIDQuMSw0LjF6bTQuMS00NC4zaDM2LjF2MzYuMWgtMzYuMXYtMzYuMXoiIGZpbGw9IiMwMDAwMDAiLz4KICAgICAgPHBhdGggZD0ibTEyMi42LDEwLjVjMC0yLjMtMS44LTQuMS00LjEtNC4xaC00NC4zYy0yLjMsMC00LjEsMS44LTQuMSw0LjF2NDQuM2MwLDIuMyAxLjgsNC4xIDQuMSw0LjFoNDQuM2MyLjMsMCA0LjEtMS44IDQuMS00LjF2LTQ0LjN6bS04LjIsNDAuMmgtMzYuMXYtMzYuMWgzNi4xdjM2LjF6IiBmaWxsPSIjMDAwMDAwIi8+CiAgICAgIDxwYXRoIGQ9Im0xMC41LDEyMi42aDQ0LjNjMi4zLDAgNC4xLTEuOCA0LjEtNC4xdi00NC4zYzAtMi4zLTEuOC00LjEtNC4xLTQuMWgtNDQuM2MtMi4zLDAtNC4xLDEuOC00LjEsNC4xdjQ0LjNjMCwyLjIgMS45LDQuMSA0LjEsNC4xem00LjEtNDQuM2gzNi4xdjM2LjFoLTM2LjF2LTM2LjF6IiBmaWxsPSIjMDAwMDAwIi8+CiAgICAgIDxwYXRoIGQ9Im0xMTguNSw3MC4xaC00NC4zYy0yLjMsMC00LjEsMS44LTQuMSw0LjF2NDQuM2MwLDIuMyAxLjgsNC4xIDQuMSw0LjFoNDQuM2MyLjMsMCA0LjEtMS44IDQuMS00LjF2LTQ0LjNjMC0yLjItMS45LTQuMS00LjEtNC4xem0tNC4xLDQ0LjNoLTM2LjF2LTM2LjFoMzYuMXYzNi4xeiIgZmlsbD0iIzAwMDAwMCIvPgogICAgPC9nPgogIDwvZz4KPC9zdmc+Cg==)
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='342.728' height='325.878' viewBox='0 0 90.68 86.222' fill='none' stroke='%23000' stroke-width='5.6' stroke-linejoin='round' stroke-dashoffset='22.7149601' xmlns:v='https://vecta.io/nano'%3E%3Cpath d='M3.15 3.15h37.378v35.24H3.15zm47.002 0H87.53v35.24H50.152zM3.15 47.832h37.378v35.24H3.15zm47.002 0H87.53v35.24H50.152z'/%3E%3C/svg%3E");
}
#container-2 {
background-repeat: no-repeat;
background-size: 80px;
background-position: 50% 65%;
background-image: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMjkgMTI5IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMjkgMTI5IiB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiPgogIDxnPgogICAgPHBhdGggZD0ibTExOC41LDEwLjVoLTEwOGMtMi4zLDAtNC4xLDEuOC00LjEsNC4xdjUxLjcgMjEuMWMwLDIuMyAxLjgsNC4xIDQuMSw0LjFoNDkuOXYxOC44aC0yMi45Yy0yLjMsMC00LjEsMS44LTQuMSw0LjFzMS44LDQuMSA0LjEsNC4xaDU0YzIuMywwIDQuMS0xLjggNC4xLTQuMXMtMS44LTQuMS00LjEtNC4xaC0yMi45di0xOC44aDQ5LjljMi4zLDAgNC4xLTEuOCA0LjEtNC4xdi0yMS4xLTUxLjdjMC0yLjMtMS44LTQuMS00LjEtNC4xem0tNC4xLDcyLjhoLTk5Ljh2LTEzaDk5Ljh2MTN6bTAtMjEuMWgtOTkuOHYtNDMuNWg5OS44djQzLjV6IiBmaWxsPSIjMDAwMDAwIi8+CiAgPC9nPgo8L3N2Zz4K)
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='347.569' height='278.797' viewBox='0 0 91.961 73.765' fill='none' stroke='%23000' xmlns:v='https://vecta.io/nano'%3E%3Cpath d='M3.02 3.02h85.921v54.399H3.02z' stroke-width='6.04' stroke-linejoin='round' stroke-dashoffset='22.7149601'/%3E%3Cg stroke-width='6.3'%3E%3Cpath d='M35.607 70.527l21.839.071' stroke-linecap='round' paint-order='markers fill stroke'/%3E%3Cpath d='M46.404 73.517l.142-15.596' paint-order='markers fill stroke'/%3E%3C/g%3E%3C/svg%3E");
}
#container-3 {
background-repeat: no-repeat;
background-size: 80px;
background-size: 90px;
transition: background-image 0.3s ease-in-out;
-webkit-transition: background-image 0.3s ease-in-out;
background-position: 50% 65%;
background-image: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMjkgMTI5IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMjkgMTI5IiB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiPgogIDxnPgogICAgPHBhdGggZD0ibTk2LjYsMjYuOGgtODYuMWMtMi4yLDAtNC4xLDEuOC00LjEsNC4xdjY3LjJjMCwyLjIgMS44LDQuMSA0LjEsNC4xaDg2LjFjMi4yLDAgNC4xLTEuOCA0LjEtNC4xdi0xOS40bDE0LjksMTQuOWMwLjgsMC44IDEuOCwxLjIgMi45LDEuMiAwLjUsMCAxLjEtMC4xIDEuNi0wLjMgMS41LTAuNiAyLjUtMi4xIDIuNS0zLjh2LTUyLjVjMC0xLjYtMS0zLjEtMi41LTMuOC0xLjUtMC42LTMuMy0wLjMtNC40LDAuOWwtMTQuOSwxNC45di0xOS4zYy0wLjEtMi4zLTEuOS00LjEtNC4yLTQuMXptLTQuMSwzMy4zdjguOCAyNS4yaC03OHYtNTkuMmg3OHYyNS4yem0yMS45LTEydjMyLjlsLTEzLjctMTMuN3YtNS40bDEzLjctMTMuOHoiIGZpbGw9IiMwMDAwMDAiLz4KICA8L2c+Cjwvc3ZnPgo=)
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='347.184' height='187.007' viewBox='0 0 91.859 49.479' fill='none' stroke='%23000' stroke-width='5' stroke-linejoin='round' xmlns:v='https://vecta.io/nano'%3E%3Cpath d='M3.15 3.15h65.569v43.179H3.15z' stroke-dashoffset='22.7149601' paint-order='markers fill stroke'/%3E%3Cpath d='M68.919 28.837L88.709 44.73V7.148L69.019 22.341z'/%3E%3C/svg%3E");
}
#container-4 {
background-repeat: no-repeat;
background-size: 80px;
background-position: 50% 65%;
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgNDkwLjI4MiA0OTAuMjgyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA0OTAuMjgyIDQ5MC4yODI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxwYXRoIGQ9Ik0wLjA0MywyNDUuMTk3YzAuNiwxMC4xLDcuMywxOC42LDE3LDIxLjVsMTc5LjYsNTQuM2w2LjYsMTIzLjhjMC4zLDQuOSwzLjYsOS4yLDguMywxMC44YzEuMywwLjUsMi43LDAuNyw0LDAuNw0KCQljMy41LDAsNi44LTEuNCw5LjItNC4xbDYzLjUtNzAuM2w5MCw2Mi4zYzQsMi44LDguNyw0LjMsMTMuNiw0LjNjMTEuMywwLDIxLjEtOCwyMy41LTE5LjJsNzQuNy0zODAuN2MwLjktNC40LTAuOC05LTQuMi0xMS44DQoJCWMtMy41LTIuOS04LjItMy42LTEyLjQtMS45bC00NTksMTg2LjhDNS4xNDMsMjI1Ljg5Ny0wLjU1NywyMzUuMDk3LDAuMDQzLDI0NS4xOTd6IE0yMjYuMDQzLDQxNC4wOTdsLTQuMS03OC4xbDQ2LDMxLjgNCgkJTDIyNi4wNDMsNDE0LjA5N3ogTTM5MS40NDMsNDIzLjU5N2wtMTYzLjgtMTEzLjRsMjI5LjctMjIyLjJMMzkxLjQ0Myw0MjMuNTk3eiBNNDMyLjE0Myw3OC4xOTdsLTIyNy4xLDIxOS43bC0xNzkuNC01NC4yDQoJCUw0MzIuMTQzLDc4LjE5N3oiLz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjwvc3ZnPg0K)
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='303.594' height='274.946' viewBox='0 0 80.326 72.746' fill='none' stroke='%23000' stroke-width='4.6' stroke-linejoin='round' stroke-dashoffset='6.01000023' xmlns:v='https://vecta.io/nano'%3E%3Cpath d='M2 51.27L78.326 2l-8.03 63.359-37.093-12.414z'/%3E%3Cpath d='M33.047 70.746l.157-17.802L78.326 2 33.203 52.944l10.314 3.39z'/%3E%3C/svg%3E");
}
#container-5 {
background-repeat: no-repeat;
@ -1881,15 +1932,10 @@ audio.fileshare::-webkit-media-controls-play-button, video.fileshare::-webkit-me
vertical-align: middle;
}
#headphonesDiv {
text-align: left;
margin: 17px 0;
width: 450px;
background-color: #f3f3f3;
padding: 10px 10px;
border: 1px solid #ccc;
vertical-align: middle;
}
#effectSelector3{
background-color: #FFF;
display: inline-block;
@ -1899,7 +1945,19 @@ audio.fileshare::-webkit-media-controls-play-button, video.fileshare::-webkit-me
max-width: 100%;
width: 90%;
}
#effectsDiv {
user-select: none;
font-size:100%;
text-align:left;
margin: 17px auto;
max-width:450px;
min-width: 420px;
background-color: #f3f3f3;
display: none;
padding: 10px 10px;
border: 1px solid #ccc;
vertical-align: middle;
}
#effectsDiv3 {
text-align: left;
width: 450px;
@ -1963,10 +2021,11 @@ audio.fileshare::-webkit-media-controls-play-button, video.fileshare::-webkit-me
}
.hidden {
display:none;
display:none!important;
visibility: hidden;
width:0px;
height:0px;
opacity: 0;
}
/* visited link */
.grabLinks a:visited {
@ -1981,7 +2040,6 @@ audio.fileshare::-webkit-media-controls-play-button, video.fileshare::-webkit-me
font-size: 90%;
}
#videoSource3 {
background-color: #FFF;
display: inline-block;
vertical-align: middle;
padding: 3px;
@ -1989,7 +2047,6 @@ audio.fileshare::-webkit-media-controls-play-button, video.fileshare::-webkit-me
max-width: 370px;
}
#outputSource {
background-color: #FFF;
display: inline-block;
vertical-align: middle;
padding: 4px;
@ -1997,13 +2054,28 @@ audio.fileshare::-webkit-media-controls-play-button, video.fileshare::-webkit-me
max-width: 100%;
}
#outputSource3 {
background-color: #FFF;
display: inline-block;
vertical-align: middle;
padding: 3px;
font-size: 93%;
max-width: 100%;
}
#addPasswordBasic {
width: 450px;
white-space: nowrap;
overflow: hidden;
border-bottom: 0;
display: block;
text-align: left;
margin: 17px auto 0 auto;
max-width: 450px;
min-width: 420px;
background-color: #f3f3f3;
padding: 10px 10px;
border: 1px solid #ccc;
vertical-align: middle;
}
.videoMenu {
background-color: #f3f3f3;
width: 450px;
@ -2014,6 +2086,18 @@ audio.fileshare::-webkit-media-controls-play-button, video.fileshare::-webkit-me
text-align: left;
}
.audioMenu{
text-align: left;
margin: 17px 0px 0px;
max-width: 550px;
min-width: 420px;
background-color: rgb(243, 243, 243);
display: inline-block;
padding: 10px;
border: 1px solid rgb(204, 204, 204);
vertical-align: middle;
}
div.multiselect {
width: 450px;
white-space: nowrap;
@ -2025,6 +2109,10 @@ div.multiselect {
padding: 4px 10px 10px 10px;
background-color: #f3f3f3;
}
.form-group .multiselect {
padding: 10px;
background-color:#f3f3f3;
}
.multiselect .multiselect-contents {
display: block;
margin: 0;
@ -2347,8 +2435,7 @@ input[type=checkbox] {
.directorBlock button i {
margin-right: 5px;
}
.task {
color: #808080;
a.task {
width: 100%;
margin-top: 10px;
}
@ -2358,6 +2445,18 @@ input[type=checkbox] {
margin-left: 5px;
font-size:1.2em;
}
.shift {
display: inline-block;
position: relative;
margin: 7px 0 0 4px;
padding: 0;
width: 27px;
font-size: 0.8em;
top: -7px;
}
.shift>i {
cursor:pointer;
}
#toggleroomnotes {
grid-column: 4;
grid-row: 1;
@ -2409,10 +2508,14 @@ i.las.la-circle {
}
.streamID {
text-align: right;
margin: 5px;
font-size: 0.7em;
text-overflow: ellipsis;
margin: 5px 5px 5px 0px;
font-size: 0.7em;
text-overflow: ellipsis;
overflow: hidden;
/* left: 22px; */
position: relative;
width: 230px;
display: inline-block;
}
.streamID i {
margin-left: 5px;
@ -2444,7 +2547,14 @@ div#guestFeeds {
div#guestFeeds:empty {
display:none;
}
#hiddenElements{
visibility:hidden;
position: absolute;
left:-9999;
top:-9999;
width:0px;
height:0px;
}
#press2talk[data-enabled="true"] {
background: #1e0000;
-webkit-box-shadow: inset 0px 0px 1px #b90000;
@ -2855,9 +2965,9 @@ input:checked + .slider:before {
border: 10px dashed rgb(64 65 62)
}
#SafariWarning{
.startupWarning{
max-width:100%;
display:none;
display:block;
width: 450px;
border-left: 4px solid #eff150;
background: #fffded;
@ -2868,18 +2978,26 @@ input:checked + .slider:before {
box-shadow: 0px 5px 10px -5px #a9a9a9;
text-align: left;
}
#SafariWarning > p {
.startupWarning > p {
text-align: left;
display:inline-block;
padding-left: 38px;
}
#SafariWarning > i {
.startupWarning > i {
position: absolute;
font-size: 2em;
padding: 2px 0 0 0;
}
#consentWarning{
margin: 0 auto 20px auto;
}
#consentWarning2{
margin: 0px auto 10px auto;
}
#alertModal {
position: absolute;
background-color: rgb(221 221 221);
@ -3056,4 +3174,332 @@ input:checked + .slider:before {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
@font-face {
font-family: 'Line Awesome Free';
font-style: normal;
font-weight: 900;
font-display: auto;
src: url("./lineawesome/fonts/la-solid-900.eot");
src: url("./lineawesome/fonts/la-solid-900.eot?#iefix") format("embedded-opentype"), url("./lineawesome/fonts/la-solid-900.woff2") format("woff2"), url("./lineawesome/fonts/la-solid-900.woff") format("woff"), url("./lineawesome/fonts/la-solid-900.ttf") format("truetype"), url("./lineawesome/fonts/la-solid-900.svg#lineawesome") format("svg"); }
.las {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
display: inline-block;
font-style: normal;
font-variant: normal;
text-rendering: auto;
line-height: 1; }
.las {
font-family: 'Line Awesome Free';
font-weight: 900; }
.la-bell:before {
content: "\f0f3"; }
.la-bell-slash:before {
content: "\f1f6"; }
.la-long-arrow-alt-right:before {
content: "\f30b"; }
.la-paperclip:before {
content: "\f0c6"; }
.la-window-close:before {
content: "\f410"; }
.la-stream:before {
content: "\f550"; }
.la-file-upload:before {
content: "\f574"; }
.la-comment-alt:before {
content: "\f27a"; }
.la-tv:before {
content: "\f26c"; }
.la-volume-up:before {
content: "\f028"; }
.la-comment-dots:before {
content: "\f4ad"; }
.la-microphone:before {
content: "\f130"; }
.la-microphone-alt:before {
content: "\f3c9"; }
.la-video:before {
content: "\f03d"; }
.la-desktop:before {
content: "\f108"; }
.la-tv:before {
content: "\f26c"; }
.la-window-maximize:before {
content: "\f2d0"; }
.la-sync-alt:before {
content: "\f2f1"; }
.la-users-cog:before {
content: "\f509"; }
.la-cog:before {
content: "\f013"; }
.la-phone:before {
content: "\f095"; }
.la-skull-crossbones:before {
content: "\f714"; }
.la-hand-paper:before {
content: "\f256"; }
.la-dot-circle:before {
content: "\f192"; }
.la-bug:before {
content: "\f188"; }
.la-question-circle:before {
content: "\f059"; }
.la-language:before {
content: "\f1ab"; }
.la.la-calendar:before {
content: "\f073"; }
.la-exclamation-circle:before {
content: "\f06a"; }
.la-plug:before {
content: "\f1e6"; }
.la-ethernet:before {
content: "\f796"; }
.la-headphones:before {
content: "\f025"; }
.la-robot:before {
content: "\f544"; }
.la-info-circle:before {
content: "\f05a"; }
.la.la-hdd-o:before {
content: "\f0a0"; }
.la-key:before {
content: "\f084"; }
.la-broadcast-tower:before {
content: "\f519"; }
.la-clock:before {
content: "\f017"; }
.la-tachometer-alt:before {
content: "\f3fd"; }
.la-book-open:before {
content: "\f518"; }
.la-caret-down:before {
content: "\f0d7"; }
.la-caret-right:before {
content: "\f0da"; }
.la-copy:before {
content: "\f0c5"; }
.la-tools:before {
content: "\f7d9"; }
.la-th-large:before {
content: "\f009"; }
.la-user-circle:before {
content: "\f2bd"; }
.la-paper-plane:before {
content: "\f1d8"; }
.la-envelope:before {
content: "\f0e0"; }
.la-sign-out-alt:before {
content: "\f2f5"; }
.la-angle-right:before {
content: "\f105"; }
.la-angle-left:before {
content: "\f104"; }
.la-plus-square:before {
content: "\f0fe"; }
.la-microphone-slash:before {
content: "\f131"; }
.la-user:before {
content: "\f007"; }
.la-video-slash:before {
content: "\f4e2"; }
.la-volume-off:before {
content: "\f026"; }
.la-eye-slash:before {
content: "\f070"; }
.la-minus:before {
content: "\f068"; }
.la-plus:before {
content: "\f067"; }
.la-sync:before {
content: "\f021"; }
.la-circle:before {
content: "\f111"; }
.la-binoculars:before {
content: "\f1e5"; }
.la-stop-circle:before {
content: "\f28d"; }
.la-redo-alt:before {
content: "\f2f9"; }
.la-sliders-h:before {
content: "\f1de"; }
.la-compress-arrows-alt:before {
content: "\f78c"; }
.la-spinner:before {
content: "\f110"; }
.la.la-external-link:before {
content: "\f35d"; }
.la-pen:before {
content: "\f304"; }
.la-external-link-alt:before {
content: "\f35d"; }
.la-times:before {
content: "\f00d"; }
.la-volume-mute:before {
content: "\f6a9"; }
.la-plug:before {
content: "\f1e6"; }
.la-reply:before {
content: "\f3e5"; }
.la-headset:before {
content: "\f590"; }
.la-check:before {
content: "\f00c"; }
.la-exclamation:before {
content: "\f12a"; }
.la-chevron-down:before {
content: "\f078"; }
@media (prefers-color-scheme: dark) {
:root {
--color-mode: dark;
--background-color: #02050c;
}
}
body.darktheme .container-inner{
background-color: #40444E
}
body.darktheme .card{
background-color: #40444E
}
body.darktheme input{
background-color: #ccc;
}
body.darktheme .column>h2{
color: #b6b6b6;
}
body.darktheme .multiselect .multiselect-contents {
background-color:unset;
}
body.darktheme .directorsgrid .vidcon > .las {
background-color: #646464;
}
body.darktheme .form-group .multiselect {
background-color: #949494;
padding: 10px; background-color:#f3f3f3;
}
body.darktheme h2 {
color: #DDD;
}
body.darktheme button {
filter: brightness(0.70);
}
body.darktheme .las {
filter: brightness(0.85);
}
body.darktheme label {
filter: brightness(0.85);
}
body.darktheme #roomHeader{
filter: brightness(0.85);
}
body.darktheme div.multiselect {
background-color: #949494;
}
body.darktheme .videoMenu {
background-color: #949494;
}
body.darktheme .audioMenu{
background-color: #949494;
}
body.darktheme #headphonesDiv{
background-color: #949494;
}
body.darktheme #headphonesDiv3{
background-color: #949494;
}
body.darktheme select{
background-color: #949494;
}
body.darktheme .white {
filter: brightness(0.85);
}
body.darktheme .directorsgrid .vidcon {
background-color: #2b2b2bdd;
color: #978c8c;
}
body.darktheme .promptModalInner{
filter: brightness(0.65);
}
body.darktheme .infoblob{
color: #CCC;
}
body.darktheme #addPasswordBasic{
background-color: #949494;
}
body.darktheme #effectsDiv{
background-color: #949494;
}
body.darktheme #effectsDiv3{
background-color: #949494;
}
body.darktheme .outMessage{
background-color: #7f89a7;
}
body.darktheme .inMessage {
background-color: #b1b1b1;
}
body.darktheme #chatInput{
background-color:#ccc;
}
body.darktheme .popup-message{
background-color: #9b9b9b !important;
}
body.darktheme #audioScreenShare1{
background-color: #949494;
}
body.darktheme #webcamstats2{
background-color: #949494;
}
body.darktheme #audioSourceScreenshare{
background-color: #AAA;
}
body.darktheme #headphonesDiv2{
background-color: #949494;
}
body.darktheme #outputSourceScreenshare{
background-color: #AAA;
}
body.darktheme #audioSource{
background-color: #AAA;
}
body.darktheme #outputSource{
background-color: #AAA;
}
body.darktheme #videoSourceSelect{
background-color: #AAA;
}
body.darktheme #videoSettings{
background-color: #949494;
}
body.darktheme #effectSelector{
background-color: #AAA;
}
body.darktheme #effectSelector3{
background-color: #AAA;
}
body.darktheme #audioSource3{
background-color: #AAA;
}
body.darktheme #videoSource3{
background-color: #AAA;
}
body.darktheme #outputSource3{
background-color: #AAA;
}
body.darktheme #videoSettings3{
background-color: #949494;
}
body.darktheme .promptModalInner{
background-color: #ccc;
}
body.darktheme .alertModal{
background-color: #ccc;
filter:brightness(0.85);
}
body.darktheme .directorContainer{
filter: brightness(0.85);
}

756
main.js

File diff suppressed because it is too large Load Diff

73
media/camera_inkscape.svg Normal file
View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="91.859253mm"
height="49.479435mm"
viewBox="0 0 91.859252 49.479436"
version="1.1"
id="svg4530"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="camera_inkscape.svg">
<defs
id="defs4524" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.3235466"
inkscape:cx="338.91574"
inkscape:cy="131.01428"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:snap-text-baseline="false"
inkscape:snap-bbox="false"
inkscape:snap-global="true"
inkscape:window-width="2560"
inkscape:window-height="1361"
inkscape:window-x="2391"
inkscape:window-y="-9"
inkscape:window-maximized="1" />
<metadata
id="metadata4527">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-4.0990803,-66.079714)">
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:6.29999971;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:22.7149601;stroke-opacity:1;paint-order:markers fill stroke"
id="rect5164"
width="65.568779"
height="43.179436"
x="7.2490802"
y="69.229713" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:6.30000019;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 73.017762,94.91748 19.790574,15.89243 V 73.227808 L 73.117714,88.420574 Z"
id="path5172"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

88
media/grid_inkscape.svg Normal file
View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="90.680443mm"
height="86.222237mm"
viewBox="0 0 90.680443 86.222237"
version="1.1"
id="svg4530"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="grid_inkscape.svg">
<defs
id="defs4524" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.9358888"
inkscape:cx="-118.37523"
inkscape:cy="171.46325"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:snap-text-baseline="false"
inkscape:snap-bbox="false"
inkscape:snap-global="true"
inkscape:window-width="1558"
inkscape:window-height="815"
inkscape:window-x="3042"
inkscape:window-y="342"
inkscape:window-maximized="0" />
<metadata
id="metadata4527">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-7.8547072,-50.964769)">
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:6.30000019;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:22.7149601;stroke-opacity:1"
id="rect5084"
width="37.378147"
height="35.23999"
x="11.004707"
y="54.115173" />
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:6.30000019;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:22.7149601;stroke-opacity:1"
id="rect5084-7"
width="37.378147"
height="35.23999"
x="58.006535"
y="54.114769" />
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:6.30000019;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:22.7149601;stroke-opacity:1"
id="rect5084-5"
width="37.378147"
height="35.23999"
x="11.005169"
y="98.797012" />
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:6.30000019;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:22.7149601;stroke-opacity:1"
id="rect5084-7-8"
width="37.378147"
height="35.23999"
x="58.007"
y="98.797005" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
media/join.mp3 Normal file

Binary file not shown.

BIN
media/join.ogg Normal file

Binary file not shown.

BIN
media/join.wav Normal file

Binary file not shown.

BIN
media/leave.mp3 Normal file

Binary file not shown.

BIN
media/leave.ogg Normal file

Binary file not shown.

BIN
media/leave.wav Normal file

Binary file not shown.

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="91.960541mm"
height="73.765312mm"
viewBox="0 0 91.960541 73.765313"
version="1.1"
id="svg4530"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="monitor_inkscape.svg">
<defs
id="defs4524" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.66177331"
inkscape:cx="-137.92249"
inkscape:cy="195.10498"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:snap-text-baseline="false"
inkscape:snap-bbox="false"
inkscape:snap-global="true"
inkscape:window-width="2560"
inkscape:window-height="1361"
inkscape:window-x="2391"
inkscape:window-y="-9"
inkscape:window-maximized="1" />
<metadata
id="metadata4527">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-7.2490804,-50.638126)">
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:6.03970385;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:22.7149601;stroke-opacity:1"
id="rect5137"
width="85.920837"
height="54.398895"
x="10.268932"
y="53.657978" />
<path
style="fill:none;stroke:#000000;stroke-width:6.30000019;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
d="m 42.85551,121.16499 c 9.824106,0.14136 21.8392,0.0707 21.8392,0.0707"
id="path5160"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:6.30000019;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
d="m 53.653201,124.1546 0.141785,-15.59616"
id="path5162"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

69
media/plane_inkscape.svg Normal file
View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="80.325882mm"
height="72.74617mm"
viewBox="0 0 80.325882 72.74617"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="plane_inkscape.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="33.822827"
inkscape:cy="116.43405"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="2560"
inkscape:window-height="1361"
inkscape:window-x="2391"
inkscape:window-y="-9"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-42.368449,-95.595815)">
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6.01000023;stroke-opacity:1"
d="M 44.368022,146.86603 120.69438,97.595614 112.66457,160.95467 75.571391,148.5403 Z"
id="path3713"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6.01000023;stroke-opacity:1"
d="M 75.414599,166.34241 75.571391,148.5403 120.69438,97.595614 75.571391,148.5403 l 10.314217,3.3896 z"
id="path3715"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

11
media/thirds.svg Normal file
View File

@ -0,0 +1,11 @@
<svg width="800" height="600" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<line stroke-width="2" stroke-dasharray="5,5" stroke="#000" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_1" y2="200.2" x2="798.00001" y1="200.2" x1="2.99999" fill="none"/>
<line stroke="#000" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_3" y2="616.2" x2="266" y1="2.2" x1="266" stroke-dasharray="5,5" stroke-width="2" fill="none"/>
<line stroke-width="2" stroke-dasharray="5,5" stroke="#000" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_4" y2="200.2" x2="798.00001" y1="200.2" x1="2.99999" fill="none"/>
<line stroke="#000" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_5" y2="616.2" x2="534" y1="2.2" x1="534" stroke-dasharray="5,5" stroke-width="2" fill="none"/>
<ellipse ry="132" rx="135" id="svg_6" cy="200.43469" cx="400" stroke-dasharray="5,5" stroke-width="2" stroke="#000" fill="none"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

97
minidirector.css Normal file
View File

@ -0,0 +1,97 @@
body{
zoom: 75%;
}
.hidden{
display:unset!important;
visibility: visible;
width:unset;
height:unset;
opacity: 1;
}
button[data-action-type='solo-chat'] {
display:none! important;
}
button[data-action-type='recorder-local'] {
display:none! important;
}
span[data-action-type='ordering'] {
display:none! important;
}
button[data-action-type='open-file-share'] {
display:none! important;
}
button[data-action-type='add-channel']{
display:none! important;
}
button[data-action-type='toggle-remote-speaker']{
display:none! important;
}
button[data-action-type='toggle-remote-display']{
display:none! important;
}
button[data-action-type='hide-guest']{
display:none! important;
}
button[data-action-type='create-timer']{
display:none! important;
}
button[data-action-type='change-url']{
display:none! important;
}
button[data-action-type='change-params']{
display:none! important;
}
span[data-action-type='change-quality']{
display:none! important;
}
span[data-action-type='sceneCluster2']{
display:none! important;
}
span[data-action-type='sceneCluster1']{
display:none! important;
}
.orderspan{
display:none! important;
}
#roomHeader{
display:none! important;
}
.directorContainer {
display:none!important;
}
.hideDropMenu{
display:none!important;
}
#header{
display:none!important;
}
body {
background-color: #000;
}
button[class="pull-right"]{
display:none! important;
}
div#guestFeeds {
padding: 1px!important;
margin: 1px!important;
}
div[class="vidcon directorMargins"] {
padding: 1px!important;
margin: 1px!important;
width: 260px;
}
button[data-action-type]{
margin: 1px!important;
}

View File

@ -360,11 +360,29 @@
updateData("retransmit", 0, UUID);
}
if (e.data.remoteStats[UUID].info.label){
if (!document.getElementById("label_"+UUID)){
document.getElementById(UUID).innerHTML = "<h1 class='small' id='label_"+UUID+"'></h1>" + document.getElementById(UUID).innerHTML;
var streamID = false;
if ("streamID" in e.data) {
streamID = e.data.streamID;
}
if (document.getElementById(UUID)){
if ("label" in e.data.remoteStats[UUID].info){
if (e.data.remoteStats[UUID].info.label){
if (!document.getElementById("label_"+UUID)){
document.getElementById(UUID).innerHTML = "<h1 class='small' id='label_"+UUID+"'></h1>" + document.getElementById(UUID).innerHTML;
}
document.getElementById("label_"+UUID).innerText = e.data.remoteStats[UUID].info.label + ", - a remote viewer of stream ID: "+streamID;
} else if (streamID){
if (!document.getElementById("label_"+UUID)){
document.getElementById(UUID).innerHTML = "<h1 class='small' id='label_"+UUID+"'></h1>" + document.getElementById(UUID).innerHTML;
}
document.getElementById("label_"+UUID).innerText = "Stats for a remote viewer of stream ID: "+streamID;
} else {
if (!document.getElementById("label_"+UUID)){
document.getElementById(UUID).innerHTML = "<h1 class='small' id='label_"+UUID+"'></h1>" + document.getElementById(UUID).innerHTML;
}
document.getElementById("label_"+UUID).innerText = "";
}
}
document.getElementById("label_"+UUID).innerText = e.data.remoteStats[UUID].info.label;
}

425
nl.json Normal file
View File

@ -0,0 +1,425 @@
{
"titles": {
"toggle-the-chat": "Chat aan/uit",
"mute-the-speaker": "Demp de Spreker",
"mute-the-mic": "Demp de Mic",
"disable-the-camera": "Camera uitzetten",
"settings": "Instellingen",
"hangup-the-call": "Gesprek ophangen",
"show-help-info": "Geef Help weer",
"language-options": "Taal opties",
"tip-hold-ctrl-command-to-select-multiple": "tip: Houd CTRL (command) vast om meerdere te selecteren",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Ideaal voor 1080p60 game streaming, als de pc krachtig genoeg is",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Betere videocompressie en kwaliteit ten koste van verhoogde CPU encodering belasting",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Schakel digitale audio effecten uit en verhoogt audio bitrate",
"the-guest-will-not-have-a-choice-over-audio-options": "Gasten kunnen audio opties niet wijzigen",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "Gast kan alleen een webcam selecteren",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Scroll in en uit door CTRL vast te houden en te scrollen met d emuis (wanneer mogelijk)",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Voeg een wachtwoord toe en eis deze voor toegang",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Gast toevoegen aan groepschat.(Word automatisch gemaakt)",
"customize-the-room-settings-for-this-guest": "Geef (kamer) instellingen op voor deze gast",
"hold-ctrl-or-cmd-to-select-multiple-files": "Meerdere selecteren? CTRL vasthouden tijdens selecteren",
"enter-an-https-url": "Voer een HTTPS URL in",
"lucy-g": "Lucy G",
"flaticon": "Flaticon",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"gregor-cresnar": "Gregor Cresnar",
"add-this-video-to-any-remote-scene-1-": "Voeg deze video toe aan iedereen met '&scene=1'",
"forward-user-to-another-room-they-can-always-return-": "Stuur gast door naar andere kamer,deze kan terugkeren.",
"start-recording-this-stream-experimental-views": "Stream opnemen starten. *experimenteel*",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Forceer einde verbinding, kan wel opnieuw verbinden.",
"change-this-audio-s-volume-in-all-remote-scene-views": "Veranderd dit audio volume in alle '&scene' weergaven",
"remotely-mute-this-audio-in-all-remote-scene-views": "Op afstand dempen van deze audio op alle '&scene' weergavens",
"disable-video-preview": "Schakel Video Preview uit",
"low-quality-preview": "Lage kwaliteit Preview",
"high-quality-preview": "Hoge kwaliteit Preview",
"send-direct-message": "Stuur een prive bericht",
"advanced-settings-and-remote-control": "Geavanceerde instellingen en Remote Control",
"toggle-voice-chat-with-this-guest": "Schakel geluids chat aan/uit met deze gast",
"join-by-room-name-here": "Voer een naam in voor snelle toegang",
"join-room": "Ga de kamer in",
"share-a-screen-with-others": "Deel je scherm met anderen",
"alert-the-host-you-want-to-speak": "Geef de director een seintje",
"record-your-stream-to-disk": "Streamopname naar lokale opslag",
"cancel-the-director-s-video-audio": "Annuleer de Director zijn Video/Audio",
"submit-any-error-logs": "Verzend foutmeldingslog",
"add-group-chat-to-obs": "Voeg een groepschat toe aan OBS",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "Bij grotere groepen kan deze optie voor een lagere belasting aan gast zijde zorgen",
"which-video-codec-would-you-want-used-by-default-": "Welke video codec wil je standaard gebruiken?",
"you-ll-enter-as-the-room-s-director": "Je gaat de kamer binnen als Director",
"add-your-camera-to-obs": "Voeg je camera aan OBS toe",
"remote-screenshare-into-obs": "Schermdelen naar OBS",
"create-reusable-invite": "creëer herbruikbaare link",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "Coder de link zo dat het moeilijker is voor de gast om er achter te komen welke instellingen ingesteld staan",
"more-options": "Meer opties",
"youtube-video-demoing-how-to-do-this": "Youtube video voorbeelden, hoe dit te doen!",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Vraag een gast zijn beeld te publiceren in de kamer",
"if-enabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "Wanneer deze optie aan staat kan de gast niemand zien of horen",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Gebruik deze link als OBS Browser source om video/audio binnen te halen",
"if-enabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "Wanneer deze optie aanstaat moet je video handmatig toevoegen aan een scene om hem te zien.",
"disables-echo-cancellation-and-improves-audio-quality": "Schakelt Echo Cancellation uit en verbetert audio kwaliteit",
"audio-only-sources-are-visually-hidden-from-scenes": "Bronnen met alleen audio niet laten zien in de scene",
"guest-will-be-prompted-to-enter-a-display-name": "Vraag gasten om een naam om weer te geven",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Namen van gasten komen links onder in de hoek van de video",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "Vraag om 1080p60 i.p.v. 720p60, wanneer mogelijk",
"the-default-microphone-will-be-pre-selected-for-the-guest": "Voor deze gast standaard microfoon selecteren vooraf",
"the-default-camera-device-will-selected-automatically": "Standaard camera word automatisch geselecteerd",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "De gast kan hierdoor niet wisselen tussen camera en scherm delen",
"the-guest-will-not-see-their-own-self-preview-after-joining": "Hierdoor zit de gast zichzelf niet nadat deze het gesprek is binnengekomen",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Gasten krijgen een optie de director een verzoek tot aandacht te sturen",
"add-an-audio-compressor-to-the-guest-s-microphone": "Voeg een audio compressor toe aan de gast zijn microfoon",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Voeg een audio equalizer toe aan gast, onder controle van director",
"the-guest-can-only-see-the-director-s-video-if-provided": "Gat kan alleen de director zien,wanneer deze aanwezig is",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "Microfoon van gast standaard uit, kunnen zichzelf wel aanzetten",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Microfoon van gast standaard uit, alleen director kan deze aanzetten",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Encodeer de invitatie URL. Om aanpassingen door gast moeilijker te maken",
"move-the-user-to-another-room-controlled-by-another-director": "Verplaats gast naar andere room. Beheerd door een andere director",
"send-a-direct-message-to-this-user-": "Stuur een Direct Message naar deze gast",
"remotely-change-the-volume-of-this-guest": "Op afstand veranderen volume gast",
"mute-this-guest-everywhere": "Deze gast niet meer hoorbaar maken overal",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Start opname van de remote audio/video stream op de lokale drive *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "Zet opname aan bij gasten zelf op lokale schijf van gast *experimental*",
"shift-this-video-down-in-order": "Schuif deze video lager in orde",
"current-index-order-of-this-video": "Huidige index plek van deze video",
"shift-this-video-up-in-order": "Schuif deze video hoger in orde",
"remote-audio-settings": "Audio instellingen op afstand",
"advanced-video-settings": "Geavanceerde instellingen van video",
"activate-or-reload-this-video-device-": "Activeer of herlaadt deze video bron.",
"create-a-secondary-stream": "Maak een tweede stream aan",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "De director zal zichtbaar zijn in de scenes, alsof hij ook een gast is.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Handig als je de director en gast tegelijk wilt zijn.",
"start-streaming": "start streaming",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "Waneer uitgeschakeld, zal de uitgenodigde gast niemand kunnen horen of zien in de kamer.",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "Wanneer uitgeschakeld, moet je handmatig eeen video toevoegen aan een scene voordat deze zichtbaar wordt.",
"toggle-solo-voice-chat": "Schakel Solo Geluids Chat",
"toggle-the-remote-guest-s-speaker-output": "Schakel de gast's speaker uitgang",
"toggle-the-remote-guest-s-display-output": "Schakel de gast's beeld uitgang",
"load-the-next-guest-in-queue": "Load the next guest in queue",
"add-to-calendar": "Add to Calendar",
"guests-not-actively-speaking-will-be-hidden": "Guests not actively speaking will be hidden",
"increase-video-quality-that-guests-in-room-see-": "Increase video quality that guests in room see.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "The guest will not be asked for a video device on connection",
"add-to-scene-2": "Add to Scene 2",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"set-to-default-audio-channel": "Set to Default Audio Channel",
"set-to-audio-channel-1": "Set to Audio Channel 1",
"set-to-audio-channel-2": "Set to Audio Channel 2",
"set-to-audio-channel-3": "Set to Audio Channel 3",
"set-to-audio-channel-4": "Set to Audio Channel 4",
"set-to-audio-channel-5": "Set to Audio Channel 5",
"set-to-audio-channel-6": "Set to Audio Channel 6",
"set-to-audio-channel-7": "Set to Audio Channel 7",
"set-to-audio-channel-8": "Set to Audio Channel 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"solo-this-video-everywhere": "Solo this video everywhere",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"cannot-see-videos": "Cannot see videos",
"cannot-hear-others": "Cannot hear others",
"see-director-only": "See director only",
"show-mini-preview": "Show Mini preview",
"raise-hand-button": "Raise hand button",
"show-labels": "Show labels",
"transfer-to-a-new-room": "Transfer to a new Room",
"enable-custom-password": "Enable custom password",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"add-this-video-to-any-remote-scene-2-": "Add this Video to any remote '&scene=2'",
"add-to-scene-8": "Add to Scene 8",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees",
"enter-a-room-name-to-quick-join": "Enter a room name to quick join"
},
"innerHTML": {
"logo-header": "<font id=\"qos\" style=\"color: white;\">O</font>BS Ninja",
"copy-this-url": "Deelbare Link naar deze video",
"you-are-in-the-control-center": "U bent in het kamer beheers centrum",
"joining-room": "U neemt deel aan de kamer",
"add-group-chat": "Voeg groepsgesprek toe",
"rooms-allow-for": "Kamers maken eenvoudige groepsgespreken en geavanceerd beheer van meerdere streams tegelijkertijd mogelijk.",
"room-name": "Kamer Naam",
"password-input-field": "Password",
"enter-the-rooms-control": "Ga de Kamer's Controle Centrum in",
"show-tips": "Toon me wat tips..",
"added-notes": "\n\t\t\t\t<u><i>Notities:</i></u>\n\t\t\t\t<li>Iedereen kan de kamer binnenkomen als ze de naam kennen, dus hou hem uniek</li>\n\t\t\t\t<li>Meer dan vier (4) mensen in een kamer is niet aan te raden vanwege prestatie redenen, maar is afhankelijk van uw hardware.</li>\n\t\t\t\t<li>Bij iOS apparaten is de video alleen zichtbaar voor de regiseur. Dit is een hardware beperking.</li>\n\t\t\t\t<li>De \"Opname\" optie is nieuw en is experimenteel.</li>\n\t\t\t\t<li>U moet een video stroom \"Toevoegen\" aan de \"Groeps Scene\" om het hier te tonen.</li>\n\t\t\t\t<li>Er is een nieuwe \"uitgebreid volledig scherm\" knop toegevoegd aan het Gasten scherm.</li>\n\t\t\t\t",
"back": "Terug",
"add-your-camera": "Voeg je Camera toe",
"ask-for-permissions": "Geef toestemming voor gebruik Camera/Microfoon",
"waiting-for-camera": "Wachten op het Laden van de Camera",
"video-source": "Video bron",
"max-resolution": "Max Resolutie",
"balanced": "Gebalanceerd",
"smooth-cool": "Soepel en Koel",
"select-audio-source": "Selecteer Geluidsbronnen",
"no-audio": "Geen Geluid",
"select-output-source": " Audio Output Destination: \n\t\t\t\t\t",
"remote-screenshare-obs": "Deel externe scherm",
"note-share-audio": "\n\t\t\t\t\t<b>Noot</b>: Vergeet niet op \"Deel geluid\" te klikken in Chrome.<br>(Firefox ondersteung geen geluid delen.)",
"select-screen-to-share": "SELECTEER SCHERM OM TE DELEN",
"audio-sources": "Geluidsbronnen",
"create-reusable-invite": "Maak Herbruikbare Uitnodiging",
"here-you-can-pre-generate": "Hier kan u vooraf een herbruikbare weergave link en een bijbehorende gast uitnodigingslink aanmaken.",
"generate-invite-link": "GENEREER DE UITNODIGINGS LINK",
"advanced-paramaters": "Geavanceerde Parameters",
"unlock-video-bitrate": "Verwijder limitatie video bitrate (20mbps)",
"force-vp9-video-codec": "Forceer VP9 Video Codec (minder verstoring)",
"enable-stereo-and-pro": "Activeer Stereo en Pro HD Geluid",
"video-resolution": "Video Resolutie: ",
"hide-mic-selection": "Forceer Standaard Microfoon",
"hide-screen-share": "Verberg Scherm Delen Optie",
"allow-remote-control": "Afstandsbediening Camera Zoom (android)",
"add-a-password-to-stream": " Add a password:",
"add-the-guest-to-a-room": " Voeg de gast toe aan een kamer:",
"invite-group-chat-type": "Deze kamer gast kan:",
"can-see-and-hear": "Het groepsgesprek zien en horen",
"can-hear-only": "Alleen het groepsgesprek horen",
"cant-see-or-hear": "Het groepsgesprek niet horen en zien",
"share-local-video-file": "Stream Media Bestand",
"share-website-iframe": "Deel Website",
"run-a-speed-test": "Doe een Speed Test",
"read-the-guides": "Blader in de handleidingen",
"info-blob": "\n\t\t\t\t\t\t<h2>Wat is OBS.Ninja</h2><br>\n\t\t\t\t\t\t<li>100% <b>gratis</b>; geen downloads; geen persoonlijke gegevens verzamelen; niet inloggen</li>\n\t\t\t\t\t\t<li>Breng video van uw smartphone, laptop, computer, of van uw vrienden direct in uw OBS video stroom</li>\n\t\t\t\t\t\t<li>We gebruiken vooruitstrevende Peer-to-Peer technologie die privacy en ultra lage vertraging biedt</li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\t<li>Youtube video <i class=\"fa fa-youtube-play\" aria-hidden=\"true\"></i> <a href=\"https://www.youtube.com/watch?v=6R_sQKxFAhg\">Demonstratie</a> </li>\n\t\t\t\t\t\t",
"add-to-scene": "Toevoegen aan Scene",
"forward-to-room": "Doorverbinden",
"record": "Neem op",
"disconnect-guest": "Ophangen",
"mute": "Demp",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Bericht",
"advanced-camera-settings": "<i class=\"las la-cog\"></i> Geavanceerd",
"voice-chat": "<i class=\"las la-microphone\"></i> Geluids Chat",
"open-in-new-tab": "Open in nieuw tabblad",
"copy-to-clipboard": "Kopiëren naar klembord",
"click-for-quick-room-overview": "❔ Klik hier voor een snel overzicht en hulp",
"push-to-talk-enable": "🔊 Enable Director's Push-to-Talk Mode Zet ",
"welcome-to-control-room": "Welcome. This is the control-room for the group-chat. There are different things you can use this room for:<br><br>\t<li>You can host a group chat with friends using a room. Share the blue link to invite guests who will join the chat automatically.</li>\t<li>A group room can handle around 4 to 30 guests, depending on numerous factors, including CPU and available bandwidth of all guests in the room.</li>\t<li>Solo-views of each video are offered under videos as they load. These can be used within an OBS Browser Source.</li>\t<li>You can use the auto-mixing Group Scene, the green link, to auto arrange multiple videos for you in OBS.</li>\t<li>You can use this control room to record isolated video or audio streams, but it is an experimental feature still.</li>\t<li>Videos in the Director's room will be of low quality on purpose; to save bandwidth/CPU</li>\t<li>Guest's in the room will see each other's videos at a very limited quality to conserve bandwidth/CPU.</li>\t<li>OBS will see a guest's video in high-quality; the default video bitrate is 2500kbps.</li>\t<br>\tAs guests join, their videos will appear below. You can bring their video streams into OBS as solo-scenes or you can add them to the Group Scene.\t<br>The Group Scene auto-mixes videos that have been added to the group scene. Please note that the Auto-Mixer requires guests be manually added to it for them to appear in it; they are not added automatically.<br><br>Apple mobile devices, such as iPhones and iPads, do not fully support Video Group Chat. This is a hardware constraint.<br><br>\tFor advanced options and parameters, <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">see the Wiki.</a>",
"more-than-four-can-join": "Er staan momenteel vier gasten plekken gevuld voor de demonstratie. Het is mogelijk om meer gasten te hebben in een kamer.",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tWelcome to OBS.Ninja! You can send text messages directly to connected peers from here.\n\t\t\t\t",
"names-and-labels-coming-soon": "\n\t\t\t\t\tNames identifying connected peers will be a feature in an upcoming release.\n\t\t\t\t",
"send-chat": "Verstuur",
"available-languages": "Beschikbare talen:",
"add-more-here": "Voer hier meer toe!",
"waiting-for-camera-to-load": "Wachten tot camera geladen is",
"start": "START",
"share-your-mic": "Deel je microfoon",
"share-your-camera": "Deel je camera",
"share-your-screen": "Deel je scherm",
"join-room-with-mic": "Ga de kamer binnen met microfoon",
"share-screen-with-room": "Deel je scherm met de kamer",
"join-room-with-camera": "Ga de kamer binnen met camera",
"click-start-to-join": "Druk op start om erin te gaan",
"guests-only-see-director": "Gasten kunnen alleen de directors video zien",
"default-codec-select": "Geprefereerde Video Codec: ",
"obfuscate_url": "Verhul originele uitnodigingslink",
"hide-the-links": " LINKS (GUEST INVITES &amp; SCENES)",
"invite-users-to-join": "Gasten kunnen de link gebruiken om de kamer binnen te gaan",
"this-is-obs-browser-source-link": "Gebruik deze link om de video mix te gebruiken in OBS of andere studio software",
"mute-scene": "mute in scene",
"mute-guest": "mute guest",
"record-local": " Lokale opname",
"record-remote": " Opname remote",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Audio instellingen",
"scenes-can-see-director": "Director is ook een gast",
"toggle-remote-speaker": "Verdoof Gast",
"toggle-remote-display": "Verblind Gast",
"select-digital-effect": " Digital Video Effects: ",
"add-a-password": " Add a Password:",
"hide-guest": "hide guest",
"force-keyframe": "Rainbow Puke",
"change-url": "Change URL",
"change-params": "URL Params",
"solo-video": "Highlight guest",
"stats-remote": " Scene Stats",
"apply-new-guest-settings": "Apply settings",
"cancel": "Cancel",
"add-to-calendar": "Add details to your Calendar:",
"no-effects-applied": "No effects applied",
"blurred-background": "Blurred background",
"digital-greenscreen": "Digital greenscreen",
"virtual-background": "Virtual background",
"use-chrome-instead": "Consider using a Chromium-based browser instead.<br>\n \t\t\t\t\t\tSafari is more prone to having audio issues",
"select-the-video-files-to-share": "SELECT THE VIDEO FILES TO SHARE",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"click-here-for-help": "Click Here for a quick overview and help",
"guests-hear-others": "Guests hear others",
"capture-a-group-scene": "CAPTURE A GROUP SCENE",
"auto-add-guests": "Auto-add guests",
"pro-audio-mode": "Pro-audio mode",
"hide-audio-only-sources": "Hide audio-only sources",
"ask-for-display-name": "Ask for display name",
"show-display-names": "Show display names",
"show-active-speaker": "Show active speakers",
"auto-select-microphone": "Auto-select default microphone",
"auto-select-camera": "Auto-select default camera",
"hide-setting-buttons": "Hide settings button",
"mini-self-preview": "Mini self-preview",
"virtual-backgrounds": "Virtual backgrounds",
"powerful-computers-only": "Only use with powerful computers and small groups!!",
"guests-see-HD-video": "Guests see HD video",
"no-self-preview": "Disable self-preview",
"raise-hand-button": "Display 'raise-hand' button",
"enable-compressor": "Enable audio compressor",
"enable-equalizer": "Enable equalizer as option",
"low-cpu=broadcast-codec": "Low-CPU broadcast codec",
"only-see-director-feed": "Only see the director's feed",
"mute-microphone-by-default": "Mute microphone by default",
"guest-joins-with-no-camera": "Guest joins with no camera",
"unmute-by-director-only": "Unmute by director only",
"obfuscate-link": "Obfuscate link and parameters",
"this-can-reduce-packet-loss": "This can reduce video corruption caused by packet loss",
"use-h264-codec": "Use H264 codec",
"show-active-speakers": "Show active speakers",
"force-mono-audio": "Force mono audio",
"learn-more-about-params": "Learn more about URL parameters at ",
"More-scene-options": "More scene options",
"additional-controls": "Additional controls",
"select-local-image": "Select Local Image",
"close-settings": "Close Settings",
"advanced": "Advanced ",
"invisible-guests": "Not Visible",
"add-to-google-calendar": "Add to Google Calendar",
"add-to-outlook-calendar": "Add to Outlook Calendar",
"add-to-yahoo-calendar": "Add to Yahoo Calendar",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"fade-videos-in": "Fade videos in",
"show-guest-tips": "Show guest setup tips",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"add-to-scene2": "add to scene 2",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast..",
"join-room": "Join room",
"waiting-for-mic-to-load": "Waiting for mic to load"
},
"placeholders": {
"join-by-room-name-here": "Ga binnen met een kamer naam",
"enter-a-room-name-here": "Geef hier een kamer naam op",
"optional-room-password-here": "Optionele wachtwoord voor kamer",
"give-this-media-source-a-name-optional-": "Geef de media bron een naam (optioneel)",
"add-an-optional-password": "Voeg optioneel wachtwoord toe",
"enter-room-name-here": "Geef hier de kamer naam op",
"enter-chat-message-to-send-here": "Type hier om te chatten",
"optional": "optional",
"enter-the-room-name-here": "Enter the room name here",
"enter-the-room-password-here": "Enter the room password here",
"enter-your-message-here": "Enter your message here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "Enter a new Display Name for this stream",
"submit-error-report": "Press OK to submit any error logs to VDO.Ninja. Error logs may contain private information.",
"director-redirect-1": "The director wishes to redirect you to the URL: ",
"director-redirect-2": "\n\nPress OK to be redirected.",
"add-a-label": "Add a label",
"audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. You can try claiming the room after the first director leaves.</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.",
"director": "Director",
"unknown-user": "Unknown User",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?",
"load-previous-session": "Would you like to load your previous session's settings?",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.",
"enter-display-name": "Please enter your display name:",
"enter-new-display-name": "Enter a new Display Name for this stream",
"what-bitrate": "What bitrate would you like to record at? (kbps)",
"enter-website": "Enter a website URL to share",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.",
"share-with-trusted": "Share only with those you trust",
"pass-recommended": "A password is recommended",
"insecure-room-name": "Insecure room name.",
"allowed-chars": "Allowed chars",
"transfer": "transfer",
"armed": "armed",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)",
"change-url": "change URL",
"mute-in-scene": "mute in scene",
"unmute-guest": "un-mute guest",
"undeafen": "un-deafen",
"deafen": "deafen guest",
"unblind": "un-blind",
"blind": "blind guest",
"unmute": "un-mute",
"mute-guest": "mute guest",
"unhide": "unhide guest",
"hide-guest": "hide guest",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

425
pig.json Normal file
View File

@ -0,0 +1,425 @@
{
"titles": {
"toggle-the-chat": "Toggle the Chat",
"mute-the-speaker": "Mute the Speaker",
"mute-the-mic": "Mute the Mic",
"disable-the-camera": "Disable the Camera",
"settings": "Settings",
"hangup-the-call": "Hangup the Call",
"show-help-info": "Show Help Info",
"language-options": "Language Options",
"tip-hold-ctrl-command-to-select-multiple": "tip: Hold CTRL (command) to select Multiple",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Ideal for 1080p60 gaming, if your computer and upload are up for it",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Better video compression and quality at the cost of increased CPU encoding load",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Disable digital audio-effects and increase audio bitrate",
"the-guest-will-not-have-a-choice-over-audio-options": "The guest will not have a choice over audio-options",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "The guest will only be able to select their webcam as an option",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Hold CTRL and the mouse wheel to zoom in and out remotely of compatible video streams",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Add a password to make the stream inaccessible to those without the password",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Add the guest to a group-chat room; it will be created automatically if needed.",
"customize-the-room-settings-for-this-guest": "Customize the room settings for this guest",
"hold-ctrl-or-cmd-to-select-multiple-files": "Hold CTRL (or CMD) to select multiple files",
"enter-an-https-url": "Enter an HTTPS URL",
"lucy-g": "Lucy G",
"flaticon": "Flaticon",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"gregor-cresnar": "Gregor Cresnar",
"add-this-video-to-any-remote-scene-1-": "Add this Video to any remote '&scene=1'",
"forward-user-to-another-room-they-can-always-return-": "Forward user to another room. They can always return.",
"start-recording-this-stream-experimental-views": "Start Recording this stream. *experimental*' views",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Feasonsray",
"change-this-audio-s-volume-in-all-remote-scene-views": "Ceasonsray",
"remotely-mute-this-audio-in-all-remote-scene-views": "Reeasonsray",
"disable-video-preview": "Deasonsray",
"low-quality-preview": "Leasonsray",
"high-quality-preview": "Heasonsray",
"send-direct-message": "Seasonsray",
"advanced-settings-and-remote-control": "Aeasonsray",
"toggle-voice-chat-with-this-guest": "Teasonsray",
"join-by-room-name-here": "Enter a room name to quick join",
"join-room": "Join room",
"share-a-screen-with-others": "Share a Screen with others",
"alert-the-host-you-want-to-speak": "Alert the host you want to speak",
"record-your-stream-to-disk": "Record your stream to disk",
"cancel-the-director-s-video-audio": "Cancel the Director's Video/Audio",
"submit-any-error-logs": "Submit any error logs",
"add-group-chat-to-obs": "Add Group Chat to OBS",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "For large group rooms, this option can reduce the load on remote guests substantially",
"which-video-codec-would-you-want-used-by-default-": "Which video codec would you want used by default?",
"you-ll-enter-as-the-room-s-director": "You'll enter as the room's director",
"add-your-camera-to-obs": "Add your Camera to OBS",
"remote-screenshare-into-obs": "Remote Screenshare into OBS",
"create-reusable-invite": "Create Reusable Invite",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "Encode the URL so that it's harder for a guest to modify the settings.",
"more-options": "More Options",
"youtube-video-demoing-how-to-do-this": "Youtube Video demoing how to do this",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Invite a guest or camera source to publish into the group room",
"if-enabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If enabled, the invited guest will not be able to see or hear anyone in the room.",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Use this link in the OBS Browser Source to capture the video or audio",
"if-enabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If enabled, you must manually add a video to a scene for it to appear.",
"disables-echo-cancellation-and-improves-audio-quality": "Disables Echo Cancellation and improves audio quality",
"audio-only-sources-are-visually-hidden-from-scenes": "Audio-only sources are visually hidden from scenes",
"guest-will-be-prompted-to-enter-a-display-name": "Guest will be prompted to enter a Display Name",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Display Names will be shown in the bottom-left corner of videos",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "Request 1080p60 from the Guest instead of 720p60, if possible",
"the-default-microphone-will-be-pre-selected-for-the-guest": "The default microphone will be pre-selected for the guest",
"the-default-camera-device-will-selected-automatically": "The default camera device will selected automatically",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "The guest won't have access to changing camera settings or screenshare",
"the-guest-will-not-see-their-own-self-preview-after-joining": "The guest will not see their own self-preview after joining",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Guests will have an option to poke the Director by pressing a button",
"add-an-audio-compressor-to-the-guest-s-microphone": "Add an audio compressor to the guest's microphone",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Add an Equalizer to the guest's microphone that the director can control",
"the-guest-can-only-see-the-director-s-video-if-provided": "The guest can only see the Director's video, if provided",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "The guest's microphone will be muted on joining. They can unmute themselves.",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Have the guest join muted, so only the director can Unmute the guest.",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Make the invite URL encoded, so parameters are harder to tinker with by guests",
"move-the-user-to-another-room-controlled-by-another-director": "Move the user to another room, controlled by another director",
"send-a-direct-message-to-this-user-": "Send a Direct Message to this user.",
"remotely-change-the-volume-of-this-guest": "Remotely change the volume of this guest",
"mute-this-guest-everywhere": "Mute this guest everywhere",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Start Recording this remote stream to this local drive. *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "The Remote Guest will record their local stream to their local drive. *experimental*",
"shift-this-video-down-in-order": "Shift this Video Down in Order",
"current-index-order-of-this-video": "Current Index Order of this Video",
"shift-this-video-up-in-order": "Shift this Video Up in Order",
"remote-audio-settings": "Remote Audio Settings",
"advanced-video-settings": "Advanced Video Settings",
"activate-or-reload-this-video-device-": "Activate or Reload this video device.",
"load-the-next-guest-in-queue": "Load the next guest in queue",
"create-a-secondary-stream": "Create a Secondary Stream",
"add-to-calendar": "Add to Calendar",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "The director will be visible in scenes, as if a performer themselves.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Useful if you want to perform and direct at the same time",
"start-streaming": "start streaming",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If disabled, the invited guest will not be able to see or hear anyone in the room.",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If disabled, you must manually add a video to a scene for it to appear.",
"guests-not-actively-speaking-will-be-hidden": "Guests not actively speaking will be hidden",
"increase-video-quality-that-guests-in-room-see-": "Increase video quality that guests in room see.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "The guest will not be asked for a video device on connection",
"toggle-solo-voice-chat": "Toggle Solo Voice Chat",
"add-to-scene-2": "Add to Scene 2",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"set-to-default-audio-channel": "Set to Default Audio Channel",
"set-to-audio-channel-1": "Set to Audio Channel 1",
"set-to-audio-channel-2": "Set to Audio Channel 2",
"set-to-audio-channel-3": "Set to Audio Channel 3",
"set-to-audio-channel-4": "Set to Audio Channel 4",
"set-to-audio-channel-5": "Set to Audio Channel 5",
"toggle-the-remote-guest-s-speaker-output": "Toggle the remote guest's speaker output",
"toggle-the-remote-guest-s-display-output": "Toggle the remote guest's display output",
"set-to-audio-channel-6": "Set to Audio Channel 6",
"set-to-audio-channel-7": "Set to Audio Channel 7",
"set-to-audio-channel-8": "Set to Audio Channel 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"solo-this-video-everywhere": "Solo this video everywhere",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"cannot-see-videos": "Cannot see videos",
"cannot-hear-others": "Cannot hear others",
"see-director-only": "See director only",
"show-mini-preview": "Show Mini preview",
"raise-hand-button": "Raise hand button",
"show-labels": "Show labels",
"transfer-to-a-new-room": "Transfer to a new Room",
"enable-custom-password": "Enable custom password",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"add-this-video-to-any-remote-scene-2-": "Add this Video to any remote '&scene=2'",
"add-to-scene-8": "Add to Scene 8",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees",
"enter-a-room-name-to-quick-join": "Enter a room name to quick join"
},
"innerHTML": {
"logo-header": "<font id=\"qos\" style=\"color: white;\">O</font>BS Ninja - Pig Latin",
"copy-this-url": "Arableshay Inklay otay isthay ideovay",
"you-are-in-the-control-center": "Ouyay areway inway ethay oom'sray ontrolcay entercay",
"joining-room": "Ouyay areway oiningjay oomray",
"add-group-chat": "Addway Oupgray Atchay",
"rooms-allow-for": "Oomsray allowway orfay implifiedsay oupgray-atchay andway ethay advancedway anagementmay ofway ultiplemay eamsstray atway onceway.",
"room-name": "Oomray Amenay",
"password-input-field": "Password",
"enter-the-rooms-control": "Enterway ethay Oom'sray Ontrolcay Entercay",
"show-tips": "Owshay emay omesay ipstay..",
"added-notes": "\n\t\t\t\t<u><i>Addedway Otesnay:</i></u>\n\t\t\t\t<li>Anyoneway ancay enterway away oomray ifway eythay owknay ethay amenay, osay eepkay itway uniqueway</li>\n\t\t\t\t<li>Avinghay oremay anthay ourfay (4) eoplepay inway away oomray isway otnay advisableway ueday otay erformancepay easonsray, utbay itway ependsday onway ouryay ardwarehay.</li>\n\t\t\t\t<li>iOSWAY evicesday illway avehay eirthay ideovay onlyway ebay isiblevay otay ethay irectorday. Isthay isway away ardwarehay imitationlay.</li>\n\t\t\t\t<li>Ethay \"Ecordingray\" optionway isway ewnay andway isway onsideredcay experimentalway.</li>\n\t\t\t\t",
"back": "Ackbay",
"add-your-camera": "Addway ouryay Ameracay",
"ask-for-permissions": "Aeasonsray",
"waiting-for-camera": "Aitingway orfay Ameracay otay Oadlay",
"video-source": "Ideovay ourcesay",
"max-resolution": "Axmay Esolutionray",
"balanced": "Alancedbay",
"smooth-cool": "Oothsmay andway Oolcay",
"select-audio-source": "Electsay Audioway Ourcessay",
"no-audio": "Onay Audioway",
"select-output-source": " Audio Output Destination: \n\t\t\t\t\t",
"remote-screenshare-obs": "Emoteray Eensharescray",
"note-share-audio": "\n",
"select-screen-to-share": "ELECTSAY EENSCRAY OTAY ARESHAY",
"audio-sources": "Audioway Ourcessay",
"create-reusable-invite": "Eatecray Eusableray Inviteway",
"here-you-can-pre-generate": "Erehay ouyay ancay epray-enerategay away eusableray iewvay inklay andway away elatedray uestgay inviteway inklay.",
"generate-invite-link": "ENERATEGAY ETHAY INVITEWAY INKLAY",
"advanced-paramaters": "Advancedway Arameterspay",
"unlock-video-bitrate": "Unlockway Ideovay Itratebay (20mbpsay)",
"force-vp9-video-codec": "Orcefay VPAY9 Ideovay Odeccay (esslay artifactingway)",
"enable-stereo-and-pro": "Enableway Ereostay andway Opray HDAY Audioway",
"video-resolution": "Ideovay Esolutionray: ",
"hide-mic-selection": "Force Default Microphone",
"hide-screen-share": "Idehay Eensharescray Optionway",
"allow-remote-control": "Emoteray Ontrolcay Ameracay Oomzay (androidway)",
"add-a-password-to-stream": " Add a password:",
"add-the-guest-to-a-room": " Addway ethay uestgay otay away oomray:",
"invite-group-chat-type": "Isthay oomray uestgay ancay:",
"can-see-and-hear": "Ancay eesay andway earhay ethay oupgray atchay",
"can-hear-only": "Ancay onlyway earhay ethay oupgray atchay",
"cant-see-or-hear": "Annotcay earhay orway eesay ethay oupgray atchay",
"share-local-video-file": "Seasonsray",
"share-website-iframe": "Erehay ouyay ancay epray-enerategay",
"run-a-speed-test": "RErehay ouyay ancay epray-enerategayt",
"read-the-guides": "Brehay ouyay ancay epray-enerategayes",
"info-blob": "",
"add-to-scene": "AErehay ouyay ancay epray-enerategay",
"forward-to-room": "Erehay ouyay ancay epray-enerategay",
"record": "Ecordray",
"disconnect-guest": "Erehay ouyay ancay epray-enerategay",
"mute": "Utemay",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Erehay ouyay ancay epray-enerategay",
"advanced-camera-settings": "<i class=\"las la-cog\"></i> Erehay ouyay ancay epray-enerategay",
"voice-chat": "<i class=\"las la-microphone\"></i> Erehay ouyay ancay epray-enerategay",
"open-in-new-tab": "Openway inway ewnay Abtay",
"copy-to-clipboard": "Opycay otay Ipboardclay",
"click-for-quick-room-overview": "<i class=\"las la-question-circle\"></i> Click Here for a quick overview and help",
"push-to-talk-enable": " Enable Director's Push-to-Talk Mode",
"welcome-to-control-room": "\n\t\t\t\t\tErehay ouyay ancay epray-enerategay\n\t\t\t\t",
"more-than-four-can-join": "Erehay ouyay ancay epray-enerategay.",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tErehay ouyay ancay epray-enerategay.\n\t\t\t\t",
"names-and-labels-coming-soon": "\n\t\t\t\t\tErehay ouyay ancay epray-enerategay.\n\t\t\t\t",
"send-chat": "Erehay ouyay ancay epray-enerategay",
"available-languages": "AErehay ouyay ancay epray-enerategay:",
"add-more-here": "AErehay ouyay ancay epray-enerategaye!",
"waiting-for-camera-to-load": "waiting-for-camera-to-load",
"start": "START",
"share-your-mic": "Share your microphone",
"share-your-camera": "Share your Camera",
"share-your-screen": "Share your Screen",
"join-room-with-mic": "Join room with Microphone",
"share-screen-with-room": "Share-screen with Room",
"join-room-with-camera": "Join room with Camera",
"click-start-to-join": "Click Start to Join",
"guests-only-see-director": "Guests can only see the Director's Video",
"default-codec-select": "Preferred Video Codec: ",
"obfuscate_url": "Obfuscate the Invite URL",
"hide-the-links": " LINKS (GUEST INVITES &amp; SCENES)",
"invite-users-to-join": "Guests can use the link to join the group room",
"this-is-obs-browser-source-link": "Use in OBS or other studio software to capture the group video mix",
"mute-scene": "mute in scene",
"mute-guest": "mute guest",
"record-local": " Record Local",
"record-remote": " Record Remote",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Audio Settings",
"scenes-can-see-director": "Director will also be a performer",
"select-digital-effect": " Digital Video Effects: ",
"add-a-password": " Add a Password:",
"hide-guest": "hide guest",
"toggle-remote-speaker": "Deafen Guest",
"toggle-remote-display": "Blind Guest",
"force-keyframe": "Rainbow Puke",
"change-url": "Change URL",
"change-params": "URL Params",
"solo-video": "Highlight guest",
"stats-remote": " Scene Stats",
"apply-new-guest-settings": "Apply settings",
"cancel": "Cancel",
"add-to-calendar": "Add details to your Calendar:",
"no-effects-applied": "No effects applied",
"blurred-background": "Blurred background",
"digital-greenscreen": "Digital greenscreen",
"virtual-background": "Virtual background",
"use-chrome-instead": "Consider using a Chromium-based browser instead.<br>\n \t\t\t\t\t\tSafari is more prone to having audio issues",
"select-the-video-files-to-share": "SELECT THE VIDEO FILES TO SHARE",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"click-here-for-help": "Click Here for a quick overview and help",
"guests-hear-others": "Guests hear others",
"capture-a-group-scene": "CAPTURE A GROUP SCENE",
"auto-add-guests": "Auto-add guests",
"pro-audio-mode": "Pro-audio mode",
"hide-audio-only-sources": "Hide audio-only sources",
"ask-for-display-name": "Ask for display name",
"show-display-names": "Show display names",
"show-active-speaker": "Show active speakers",
"auto-select-microphone": "Auto-select default microphone",
"auto-select-camera": "Auto-select default camera",
"hide-setting-buttons": "Hide settings button",
"mini-self-preview": "Mini self-preview",
"virtual-backgrounds": "Virtual backgrounds",
"powerful-computers-only": "Only use with powerful computers and small groups!!",
"guests-see-HD-video": "Guests see HD video",
"no-self-preview": "Disable self-preview",
"raise-hand-button": "Display 'raise-hand' button",
"enable-compressor": "Enable audio compressor",
"enable-equalizer": "Enable equalizer as option",
"low-cpu=broadcast-codec": "Low-CPU broadcast codec",
"only-see-director-feed": "Only see the director's feed",
"mute-microphone-by-default": "Mute microphone by default",
"guest-joins-with-no-camera": "Guest joins with no camera",
"unmute-by-director-only": "Unmute by director only",
"obfuscate-link": "Obfuscate link and parameters",
"this-can-reduce-packet-loss": "This can reduce video corruption caused by packet loss",
"use-h264-codec": "Use H264 codec",
"show-active-speakers": "Show active speakers",
"force-mono-audio": "Force mono audio",
"learn-more-about-params": "Learn more about URL parameters at ",
"More-scene-options": "More scene options",
"additional-controls": "Additional controls",
"select-local-image": "Select Local Image",
"close-settings": "Close Settings",
"advanced": "Advanced ",
"invisible-guests": "Not Visible",
"add-to-google-calendar": "Add to Google Calendar",
"add-to-outlook-calendar": "Add to Outlook Calendar",
"add-to-yahoo-calendar": "Add to Yahoo Calendar",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"fade-videos-in": "Fade videos in",
"show-guest-tips": "Show guest setup tips",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"add-to-scene2": "add to scene 2",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast..",
"join-room": "Join room",
"waiting-for-mic-to-load": "Waiting for mic to load"
},
"placeholders": {
"join-by-room-name-here": "Erehay ouyay ancay epray-enerategay",
"enter-a-room-name-here": "Erehay ouyay ancay epray-enerategay",
"optional-room-password-here": "Erehay ouyay ancay epray-enerategay",
"give-this-media-source-a-name-optional-": "Erehay ouyay ancay epray-enerategay",
"add-an-optional-password": "Erehay ouyay ancay epray-enerategay",
"enter-room-name-here": "Erehay ouyay ancay epray-enerategay",
"enter-chat-message-to-send-here": "Erehay ouyay ancay epray-enerategay",
"optional": "optional",
"enter-the-room-name-here": "Enter the room name here",
"enter-the-room-password-here": "Enter the room password here",
"enter-your-message-here": "Enter your message here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "new-display-name",
"submit-error-report": "submit-error-report",
"director-redirect-1": "director-redirect-1",
"director-redirect-2": "\n\ndirector-redirect-2.",
"add-a-label": "add-a-labelA",
"audio-processing-disabled": "audio-processing-disabledA",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. You can try claiming the room after the first director leaves.AA</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.A",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.A",
"director": "DirectorA",
"unknown-user": "Unknown UserA",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?A",
"load-previous-session": "Would you like to load your previous session's settings?A",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)A",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)A",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.A",
"enter-display-name": "Please enter your display name:A",
"enter-new-display-name": "Enter a new Display Name for this streamA",
"what-bitrate": "What bitrate would you like to record at? (kbps)A",
"enter-website": "Enter a website URL to shareA",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)A",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: A",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscoreA",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.A",
"share-with-trusted": "Share only with those you trustA",
"pass-recommended": "A password is recommendedA",
"insecure-room-name": "Insecure room name.A",
"allowed-chars": "Allowed charsA",
"transfer": "transferA",
"armed": "armedA",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)A",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)A",
"change-url": "change URLA",
"mute-in-scene": "mute in sceneA",
"unmute-guest": "un-mute guestA",
"undeafen": "un-deafenA",
"deafen": "deafen guestA",
"unblind": "un-blindA",
"blind": "blind guestA",
"unmute": "un-muteA",
"mute-guest": "mute-guestA",
"unhide": "unhide-guestA",
"hide-guest": "hide-guestA",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

425
pt.json Normal file
View File

@ -0,0 +1,425 @@
{
"titles": {
"toggle-the-chat": "Ativar/desativar chat",
"mute-the-speaker": "Desligar som",
"mute-the-mic": "Desligar microfone",
"disable-the-camera": "Desligar câmera",
"settings": "Definições",
"hangup-the-call": "Desligar a chamada",
"show-help-info": "Mostrar ajuda",
"language-options": "Opções de língua",
"tip-hold-ctrl-command-to-select-multiple": "dica: Matenha pressionado CTRL (command) para seleção múltipla",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Ideal para gaming 1080p60, se o teu computador e upload aguentarem",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Melhor compressão de vídeo e qualidade com o custo de mais carga no CPU",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Desativar efeitos de áudio digitais e aumentar bitrate de áudio",
"the-guest-will-not-have-a-choice-over-audio-options": "O convidado não terá escolha sobre as opções de áudio",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "O convidado apenas poderá escolher a webcam como opção",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Segure CTRL e o scroll do rato para fazer zoom in e out remotamente em streams de vídeo compatíveis",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Adicione uma password para tornar o stream inacessível a quem não a tenha.",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Adicione o convidado a uma sala; será criada automaticamente se necessário.",
"customize-the-room-settings-for-this-guest": "Personalize as definições da sala para este convidado",
"hold-ctrl-or-cmd-to-select-multiple-files": "Segure CTRL (ou CMD) para selecionar mais de um ficheiro",
"enter-an-https-url": "Introduza um URL HTTPS",
"lucy-g": "Lucy G",
"flaticon": "Flaticon",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"gregor-cresnar": "Gregor Cresnar",
"add-this-video-to-any-remote-scene-1-": "Adicionar este vídeo a todas as cenas '&scene=1'",
"forward-user-to-another-room-they-can-always-return-": "Enviar convidado para outra sala. O convidado poderá voltar.",
"start-recording-this-stream-experimental-views": "Gravar stream. *experimental*",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Force o utilizador a desligar. Ele poderá reconectar.",
"change-this-audio-s-volume-in-all-remote-scene-views": "Altere o volume de áudio em todas as cenas '&scene'",
"remotely-mute-this-audio-in-all-remote-scene-views": "Tire o som em todas as cenas '&scene'",
"disable-video-preview": "Desativar Previsualização de Vídeo",
"low-quality-preview": "Previsualização de baixa qualidade",
"high-quality-preview": "Previsualização de alta qualidade",
"send-direct-message": "Enviar mensagem direta",
"advanced-settings-and-remote-control": "Opções avançadas e Controlo Remoto",
"toggle-voice-chat-with-this-guest": "Ativar/desativar conversa de voz com este convidado",
"join-by-room-name-here": "Introduza o nome de uma sala",
"join-room": "Entrar na sala",
"share-a-screen-with-others": "Partilhar o ecrã",
"alert-the-host-you-want-to-speak": "Avise o anfitrião que deseja falar",
"record-your-stream-to-disk": "Guarde o seu stream para o disco",
"cancel-the-director-s-video-audio": "Cancel the Director's Video/Audio",
"submit-any-error-logs": "Submeta registos de erros",
"add-group-chat-to-obs": "Adicionar conversa de grupo ao OBS",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "For large group rooms, this option can reduce the load on remote guests substantially",
"which-video-codec-would-you-want-used-by-default-": "Qual é o codec de vídeo que deseja usar por omissão?",
"you-ll-enter-as-the-room-s-director": "Vai entrar como o Realizador da sala",
"add-your-camera-to-obs": "Adicione a sua câmera ao OBS",
"remote-screenshare-into-obs": "Partilha de ecrã remota para OBS",
"create-reusable-invite": "Criar convite reutilizável",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "Obfuscar o URL para tornar mais difícil a um convidado modificar as definições.",
"more-options": "Mais Opções",
"youtube-video-demoing-how-to-do-this": "Vídeo de Youtube a explicar como fazer",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Convide alguém para publicar a câmera no grupo",
"if-enabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "Se ativado, o utilizador convidado não poderá ver ou ouvir ninguém na sala.",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Use este link numa fonte Browser do OBS para capturar o vídeo ou áudio",
"if-enabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "Se ativado, deve adicionar manualmente o vídeo a uma cena para ele aparecer.",
"disables-echo-cancellation-and-improves-audio-quality": "Desativa o Cancelamento de eco e melhora a qualidade de áudio",
"audio-only-sources-are-visually-hidden-from-scenes": "Fontes de áudio são visualmente escondidas das cenas",
"guest-will-be-prompted-to-enter-a-display-name": "O convidado poderá introduzir um nome que o identifique",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Os nomes serão mostrados no canto inferior esquerdo dos vídeos",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "Pedir 1080p60 do convidado, em vez de 720p60, se possível",
"the-default-microphone-will-be-pre-selected-for-the-guest": "O microfone predefinido será selecionado",
"the-default-camera-device-will-selected-automatically": "A câmera predefinida será selecionada automaticamente",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "O convidado não terá acesso a alterar as opções de câmera ou partilha de ecrã",
"the-guest-will-not-see-their-own-self-preview-after-joining": "O convidado não verá a sua própria imagem quando entrar",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Os convidados terão a opção de chamar a atenção do realizador pressionando um botão",
"add-an-audio-compressor-to-the-guest-s-microphone": "Adicionar um compressor de áudio ao microfone dos convidados",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Adicionar um equalizador ao microfone dos convidados, controlável pelo realizador",
"the-guest-can-only-see-the-director-s-video-if-provided": "O convidado apenas pode ver o vídeo do realizador, se providenciado",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "O microfone dos convidados começa em mute. Eles poderão remover o mute.",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Os convidados começam em mute, apenas o realizador pode ativar o som.",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Obfuscar o link de convite, para dificultar a manipulação por parte dos convidados",
"move-the-user-to-another-room-controlled-by-another-director": "Enviar o utilizador para outra sala, controlada pelo mesmo realizador",
"send-a-direct-message-to-this-user-": "Envie uma Mensagem Direta a este utilizador.",
"remotely-change-the-volume-of-this-guest": "Altere remotamente o volume deste utilizador",
"mute-this-guest-everywhere": "Tire o som a este utilizador em todo o lado",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Começar a gravar este stream remoto para este disco local. *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "O convidado remoto irá gravar o seu stream local para o seu disco local. *experimental*",
"shift-this-video-down-in-order": "Desça a posição deste vídeo",
"current-index-order-of-this-video": "Posição atual deste vídeo",
"shift-this-video-up-in-order": "Suba a posição deste vídeo",
"remote-audio-settings": "Opções de áudio remoto",
"advanced-video-settings": "Opções de vídeo avançadas",
"activate-or-reload-this-video-device-": "Ativar ou recarregar este dispositivo de vídeo.",
"load-the-next-guest-in-queue": "Carregar o próximo convidado da fila",
"create-a-secondary-stream": "Criar um stream secundário",
"add-to-calendar": "Adicionar ao calendário",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "O realizador será visível em todas as cenas, como se ele próprio fosse um convidado.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Útil se deseja realizar e participar ao mesmo tempo",
"start-streaming": "iniciar streaming",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "Se desativado, o convidado não poderá ver ou ouvir ninguém na sala.",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "Se desativado, deve adicionar manualmente um vídeo a uma cena para ele aparecer.",
"guests-not-actively-speaking-will-be-hidden": "Convidados que não falem serão escondidos",
"increase-video-quality-that-guests-in-room-see-": "Aumentar qualidade do vídeo que os convidados da sala vêem.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "Não será pedido um dispositivo de vídeo ao convidado ao ligar-se",
"toggle-solo-voice-chat": "Ativar chat de voz solo",
"add-to-scene-2": "Adicionar à Cena 2",
"add-to-scene-3": "Adicionar à Cena 3",
"add-to-scene-4": "Adicionar à Cena 4",
"add-to-scene-5": "Adicionar à Cena 5",
"add-to-scene-6": "Adicionar à cena 6",
"add-to-scene-7": "Adicionar à Cena 7",
"hide-this-guest-everywhere": "Esconder este convidado em todo o lado",
"set-to-default-audio-channel": "Definir para Canal de áudio por omissão",
"set-to-audio-channel-1": "Definir Canal de áudio 1",
"set-to-audio-channel-2": "Definir Canal de áudio 2",
"set-to-audio-channel-3": "Definir Canal de áudio 3",
"set-to-audio-channel-4": "Definir Canal de áudio 4",
"set-to-audio-channel-5": "Definir Canal de áudio 5",
"toggle-the-remote-guest-s-speaker-output": "Ativar/Desativar a saída de som remota do convidado",
"toggle-the-remote-guest-s-display-output": "Ativar/Desativar a saída de imagem remota do convidado",
"set-to-audio-channel-6": "Definir Canal de áudio 6",
"set-to-audio-channel-7": "Definir Canal de áudio 7",
"set-to-audio-channel-8": "Definir Canal de áudio 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Forçar o envio de uma keyframe por parte da fonte, para todas as cenas, corrigindo a imagem esborratada de pixeis.",
"remotely-reload-the-guest-s-page-with-a-new-url": "Recarregar a página do utilizador remotamente com um novo URL",
"change-user-parameters": "Mudar parâmetros do utilizador",
"solo-this-video-everywhere": "Fazer solo deste vídeo em todo o lado",
"request-the-statistics-of-this-video-in-any-active-scene": "Pedir estatísticas deste vídeo em qualquer Cena ativa",
"cannot-see-videos": "Não pode ver vídeos",
"cannot-hear-others": "Não pode ouvir outros",
"see-director-only": "Ver apenas o realizador",
"show-mini-preview": "Mostrar Mini previsualização",
"raise-hand-button": "Botão de levantar a mão",
"show-labels": "Mostrar identificadores",
"transfer-to-a-new-room": "Transferir para uma nova Sala",
"enable-custom-password": "Ativar password personalizada",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"add-this-video-to-any-remote-scene-2-": "Add this Video to any remote '&scene=2'",
"add-to-scene-8": "Add to Scene 8",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees",
"enter-a-room-name-to-quick-join": "Enter a room name to quick join"
},
"innerHTML": {
"logo-header": "<font id=\"qos\" style=\"color: white;\">O</font>BS.Ninja ",
"copy-this-url": "Copie este URL para uma \"Browser Source\" do OBS",
"you-are-in-the-control-center": "Está no Centro de Controlo da Sala",
"joining-room": "Está a entrar na sala",
"add-group-chat": "Adicionar conversa de grupo ao OBS",
"rooms-allow-for": "As Salas permitem conversas de grupo simplificadas e a gestão avançada de múltiplos streams simultâneos.",
"room-name": "Nome da Sala",
"password-input-field": "Password",
"enter-the-rooms-control": "Entrar no Centro de Controlo da Sala",
"show-tips": "Mostre-me algumas dicas..",
"added-notes": "\n\t\t\t\t<u><i>Notas adicionais:</i></u>\n\t\t\t\t<li>Qualquer pessoa pode entrar numa Sala se souber o nome, por isso mantenha-o único.</li>\n\t\t\t\t<li>Ter mais de quatro (4) pessoas numa Sala não é aconselhável devido a problemas de performance, mas depende do seu hardware.</li>\n\t\t\t\t<li>Dispositivos iOS são limitados a grupos de não mais de duas (2) pessoas. Esta é uma limitação de hardware.</li>\n\t\t\t\t<li>A opção \"Gravar\"é nova e considerada experimental.</li>\n\t\t\t\t<li>Deve \"Adicionar\" uma feed de vídeo à \"Cena de Grupo\" para que ela apareça lá.</li>\n\t\t\t\t<li>Existe um botão \"ecrã completo melhorado\" adicionado à vista de Convidado.</li>\n\t\t\t\t",
"back": "Voltar",
"add-your-camera": "Adicione a sua câmera ao OBS",
"ask-for-permissions": "Permita acesso à Câmera/Microfone",
"waiting-for-camera": "Esperando pela câmera",
"video-source": "Fonte de vídeo",
"max-resolution": "Resolução Máxima",
"balanced": "Balanceado",
"smooth-cool": "Suave e leve",
"select-audio-source": "Selecionar fontes de áudio",
"no-audio": "Sem áudio",
"select-output-source": " Saída de áudio: \n\t\t\t\t\t",
"remote-screenshare-obs": "Partilha de ecrã remota para OBS",
"note-share-audio": "\n\t\t\t\t\t<b>nota</b>: Não se esqueça de clicar em \"Partilhar áudio\" no Chrome.<br>(Firefox não suporta partilha de áudio.)",
"select-screen-to-share": "SELECIONAR ECRÃ A PARTILHAR",
"audio-sources": "Fontes de áudio",
"create-reusable-invite": "Criar convite reutilizável",
"here-you-can-pre-generate": "Aqui pode gerar um link Browser Source reutilizável e um link de convidado relacionado.",
"generate-invite-link": "GERAR O LINK DE CONVITE",
"advanced-paramaters": "Parâmetros avançados",
"unlock-video-bitrate": "Desbloquear Bitrate de Vídeo (20mbps)",
"force-vp9-video-codec": "Forçar Codec de vídeo VP9 (menos artefactos)",
"enable-stereo-and-pro": "Ativar áudio Stereo e Pro HD",
"video-resolution": "Resolução de Vídeo: ",
"hide-mic-selection": "Forçar microfone definido por omissão",
"hide-screen-share": "Esconder opção de partilhar ecrã",
"allow-remote-control": "Controlo remoto do zoom da câmera (android)",
"add-a-password-to-stream": " Adicionar uma password:",
"add-the-guest-to-a-room": " Adicionar convidado a uma sala:",
"invite-group-chat-type": "Este convidado pode:",
"can-see-and-hear": "Pode ver e ouvir o chat de grupo",
"can-hear-only": "Pode apenas ouvir o chat de grupo",
"cant-see-or-hear": "Não pode ver ou ouvir o chat de grupo",
"share-local-video-file": "Fazer Stream de ficheiro de media",
"share-website-iframe": "Partilhe um site",
"run-a-speed-test": "Corra um teste de velocidade",
"read-the-guides": "Descubra os Guias",
"info-blob": "\n\t\t\t\t\t\t<h2>O que é o OBS.Ninja</h2><br>\n\t\t\t\t\t\t<li>100% <b>grátis</b>; sem downloads; sem recolha de dados pessoais; sem login</li>\n\t\t\t\t\t\t<li>Leve vídeo do seu smartphone, portátil, computador, ou dos seus amigos diretamente para o seu stream de vídeo do OBS</li>\n\t\t\t\t\t\t<li>Usamos tecnologia de ponta de encaminhamento Peer-to-Peer que oferece privacidade e latência ultra-baixa</li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\t<li>Vídeo de youtube <i class=\"fa fa-youtube-play\" aria-hidden=\"true\"></i> <a href=\"https://www.youtube.com/watch?v=6R_sQKxFAhg\">Demoing it here</a> </li>",
"add-to-scene": "Adicionar à Cena",
"forward-to-room": "Transferir",
"record": "Gravar",
"disconnect-guest": "Desligar",
"mute": "Mute",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Enviar mensagem",
"advanced-camera-settings": "<i class=\"las la-cog\"></i> Avançadas",
"voice-chat": "<i class=\"las la-microphone\"></i> Chat de voz",
"open-in-new-tab": "Abrir num novo separador",
"copy-to-clipboard": "Copiar para área de transferência",
"click-for-quick-room-overview": "❔ Clique aqui para uma pequena apresentação e ajuda",
"push-to-talk-enable": "🔊 Ativar Push-to-talk do realizador",
"welcome-to-control-room": "Bem-vindo. Esta é a sala de controlo para o chat de grupo. Há diferentes coisas que pode fazer aqui:<br><br>\t<li>Pode hospedar um chat de grupo com amigos. Partilhe o link azul para os convidados se juntarem ao chat de forma automática.</li>\t<li>Uma sala de grupo pode hospedar entre 4 a 30 4 to 30 convidados, dependendo de inúmeros factores, incluindo CPU e largura de banda de todos os convidados na sala.</li>\t<li>Visualizações individuais de cada vídeo serão mostradas quando carregam. Estas podem ser usadas em Fontes do tipo Browser no OBS.</li>\t<li>Pode usar a cena de grupo automática, o link verde, para dispôr automaticamente os vídeos por si no OBS.</li>\t<li>Pode usar esta sala de controlo para gravar streams isolados de vídeo ou áudio, mas isto é ainda experimental.</li>\t<li>Vídeos na sala de controle são de baixa qualidade propositadamente; para poupar largura de banda/CPU</li>\t<li>Convidados na sala irão ver-se numa qualidade muito reduzida para conservar largura de banda/CPU.</li>\t<li>OBS tem acesso ao vídeo do convidado em alta qualidade; o bitrate de vídeo por omissão é 2500kbps.</li>\t<br>\tÀ medida que os convidados entram, os seus vídeos são mostrados abaixo. Pode levar os seus sinais para o OBS como cenas individuais ou pode adicioná-los à cena de grupo.\t<br>A Cena de grupo auto-mistura vídeos que lhe forem adicionados. Note que a auto-mistura requer que os convidados sejam manualmente adicionados; não são adicionados automaticamente.<br><br>Dispositivos móveis Apple, como iPhones e iPads, não suportam totalmente o Chat de Grupo. Este é um constrangimento de hardware.<br><br>\tPara opções avançadas e parâmetros, <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">veja o Wiki.</a>",
"more-than-four-can-join": "Estes quatro convidados são apenas ilustrativos. Podem juntar-se mais de quatro convidados numa sala.",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tBem vindo ao OBS.Ninja! Pode enviar mensagens diretas a quem estiver aqui ligado a partir daqui.\n\t\t\t\t",
"names-and-labels-coming-soon": "\n\t\t\t\t\tNomes a identificar as ligações será uma funcionalidade futura.\n\t\t\t\t",
"send-chat": "Enviar",
"available-languages": "Línguas disponíveis:",
"add-more-here": "Adicionar mais aqui!",
"waiting-for-camera-to-load": "À espera que a câmera fique pronta",
"start": "INICIAR",
"share-your-mic": "Partilhar o microfone",
"share-your-camera": "Partilhar a câmera",
"share-your-screen": "Partilhar o ecrã",
"join-room-with-mic": "Entrar na sala com microfone",
"share-screen-with-room": "Partilhar o ecrã com a sala",
"join-room-with-camera": "Entrar na sala com câmera",
"click-start-to-join": "Clique iniciar para se entrar",
"guests-only-see-director": "Os convidados só podem ver o vídeo do Realizador",
"default-codec-select": "Codec de Vídeo preferido: ",
"obfuscate_url": "Obfuscar o URL do convite",
"hide-the-links": " LINKS (CONVITES &amp; CENAS)",
"invite-users-to-join": "Os convidados podem user o link para entrar na sala",
"this-is-obs-browser-source-link": "Use no OBS ou outro software para capturar a mistura dos vídeos",
"mute-scene": "mute na cena",
"mute-guest": "mute ao convidado",
"record-local": " Gravação Local",
"record-remote": " Gravação Remota",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Definições de Áudio",
"scenes-can-see-director": "O realizador também será um participante",
"select-digital-effect": " Efeitos de Vídeo Digitais: ",
"add-a-password": " Adicionar uma Password:",
"hide-guest": "esconder convidado",
"toggle-remote-speaker": "Ensurdecer convidado",
"toggle-remote-display": "Cegar convidado",
"force-keyframe": "Vomitado arco-íris",
"change-url": "Mudar URL",
"change-params": "Parâmetros de URL",
"solo-video": "Destacar convidado",
"stats-remote": " Estatísticas da Cena",
"apply-new-guest-settings": "Aplicar definições",
"cancel": "Cancelar",
"add-to-calendar": "Adicionar detalhes ao seu calendário:",
"no-effects-applied": "No effects applied",
"blurred-background": "Blurred background",
"digital-greenscreen": "Digital greenscreen",
"virtual-background": "Virtual background",
"use-chrome-instead": "Consider using a Chromium-based browser instead.<br>\n \t\t\t\t\t\tSafari is more prone to having audio issues",
"select-the-video-files-to-share": "SELECT THE VIDEO FILES TO SHARE",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"click-here-for-help": "Click Here for a quick overview and help",
"guests-hear-others": "Guests hear others",
"capture-a-group-scene": "CAPTURE A GROUP SCENE",
"auto-add-guests": "Auto-add guests",
"pro-audio-mode": "Pro-audio mode",
"hide-audio-only-sources": "Hide audio-only sources",
"ask-for-display-name": "Ask for display name",
"show-display-names": "Show display names",
"show-active-speaker": "Show active speakers",
"auto-select-microphone": "Auto-select default microphone",
"auto-select-camera": "Auto-select default camera",
"hide-setting-buttons": "Hide settings button",
"mini-self-preview": "Mini self-preview",
"virtual-backgrounds": "Virtual backgrounds",
"powerful-computers-only": "Only use with powerful computers and small groups!!",
"guests-see-HD-video": "Guests see HD video",
"no-self-preview": "Disable self-preview",
"raise-hand-button": "Display 'raise-hand' button",
"enable-compressor": "Enable audio compressor",
"enable-equalizer": "Enable equalizer as option",
"low-cpu=broadcast-codec": "Low-CPU broadcast codec",
"only-see-director-feed": "Only see the director's feed",
"mute-microphone-by-default": "Mute microphone by default",
"guest-joins-with-no-camera": "Guest joins with no camera",
"unmute-by-director-only": "Unmute by director only",
"obfuscate-link": "Obfuscate link and parameters",
"this-can-reduce-packet-loss": "This can reduce video corruption caused by packet loss",
"use-h264-codec": "Use H264 codec",
"show-active-speakers": "Show active speakers",
"force-mono-audio": "Force mono audio",
"learn-more-about-params": "Learn more about URL parameters at ",
"More-scene-options": "More scene options",
"additional-controls": "Additional controls",
"select-local-image": "Select Local Image",
"close-settings": "Close Settings",
"advanced": "Advanced ",
"invisible-guests": "Not Visible",
"add-to-google-calendar": "Add to Google Calendar",
"add-to-outlook-calendar": "Add to Outlook Calendar",
"add-to-yahoo-calendar": "Add to Yahoo Calendar",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"fade-videos-in": "Fade videos in",
"show-guest-tips": "Show guest setup tips",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"add-to-scene2": "add to scene 2",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast..",
"join-room": "Join room",
"waiting-for-mic-to-load": "Waiting for mic to load"
},
"placeholders": {
"join-by-room-name-here": "Introduza aqui numa sala pelo seu nome",
"enter-a-room-name-here": "Introduza o nome da sala",
"optional-room-password-here": "Introduza password da sala (opcional)",
"give-this-media-source-a-name-optional-": "Dê um nome a esta fonte de mídia (opcional)",
"add-an-optional-password": "Introduza uma password (opcional)",
"enter-room-name-here": "Introduza nome da sala aqui",
"enter-chat-message-to-send-here": "Introduza mensagem a enviar aqui",
"optional": "opcional",
"enter-the-room-name-here": "Introduza aqui o nome da sala",
"enter-the-room-password-here": "Introduza aqui a password da sala",
"enter-your-message-here": "Enter your message here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "Enter a new Display Name for this stream",
"submit-error-report": "Press OK to submit any error logs to VDO.Ninja. Error logs may contain private information.",
"director-redirect-1": "The director wishes to redirect you to the URL: ",
"director-redirect-2": "\n\nPress OK to be redirected.",
"add-a-label": "Add a label",
"audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. You can try claiming the room after the first director leaves.</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.",
"director": "Director",
"unknown-user": "Unknown User",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?",
"load-previous-session": "Would you like to load your previous session's settings?",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.",
"enter-display-name": "Please enter your display name:",
"enter-new-display-name": "Enter a new Display Name for this stream",
"what-bitrate": "What bitrate would you like to record at? (kbps)",
"enter-website": "Enter a website URL to share",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.",
"share-with-trusted": "Share only with those you trust",
"pass-recommended": "A password is recommended",
"insecure-room-name": "Insecure room name.",
"allowed-chars": "Allowed chars",
"transfer": "transfer",
"armed": "armed",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)",
"change-url": "change URL",
"mute-in-scene": "mute in scene",
"unmute-guest": "un-mute guest",
"undeafen": "un-deafen",
"deafen": "deafen guest",
"unblind": "un-blind",
"blind": "blind guest",
"unmute": "un-mute",
"mute-guest": "mute guest",
"unhide": "unhide guest",
"hide-guest": "hide guest",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

18
readme.md Normal file
View File

@ -0,0 +1,18 @@
Welcome to the translation / language section of VDO.Ninja
You can specify a translation using this code, if deploying the code yourself. blank can be replaced with ru, en, pt, etc..
```
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
```
You can also add &ln=ru to the URL as a parameter to launch the translation that way.
There is a file called translate.js, which if you copy/paste the content (and hit enter) into the Chrome browser's console, while on VDO.Ninja, it will download the translation files.
It will add any new translation entries and add them to the bottom of the files. Please feel to correct these new translations and upload them back to github as a pull request.
In the future I will add a Github action so that this automatically occurs with new code posting.
Translation files can contain more than just language; they can contain HTML and image links, etc. This offers a basic amount of customization.
the "blank.json" file contains a minimal template. Little to no VDO.Ninja branding.

425
ru.json Normal file
View File

@ -0,0 +1,425 @@
{
"titles": {
"toggle-the-chat": "Toggle the Chat",
"mute-the-speaker": "Mute the Speaker",
"mute-the-mic": "Mute the Mic",
"disable-the-camera": "Disable the Camera",
"settings": "Settings",
"hangup-the-call": "Hangup the Call",
"show-help-info": "Show Help Info",
"language-options": "Language Options",
"tip-hold-ctrl-command-to-select-multiple": "tip: Hold CTRL (command) to select Multiple",
"ideal-for-1080p60-gaming-if-your-computer-and-upload-are-up-for-it": "Ideal for 1080p60 gaming, if your computer and upload are up for it",
"better-video-compression-and-quality-at-the-cost-of-increased-cpu-encoding-load": "Better video compression and quality at the cost of increased CPU encoding load",
"disable-digital-audio-effects-and-increase-audio-bitrate": "Disable digital audio-effects and increase audio bitrate",
"the-guest-will-not-have-a-choice-over-audio-options": "The guest will not have a choice over audio-options",
"the-guest-will-only-be-able-to-select-their-webcam-as-an-option": "The guest will only be able to select their webcam as an option",
"hold-ctrl-and-the-mouse-wheel-to-zoom-in-and-out-remotely-of-compatible-video-streams": "Hold CTRL and the mouse wheel to zoom in and out remotely of compatible video streams",
"add-a-password-to-make-the-stream-inaccessible-to-those-without-the-password": "Add a password to make the stream inaccessible to those without the password",
"add-the-guest-to-a-group-chat-room-it-will-be-created-automatically-if-needed-": "Add the guest to a group-chat room; it will be created automatically if needed.",
"customize-the-room-settings-for-this-guest": "Customize the room settings for this guest",
"hold-ctrl-or-cmd-to-select-multiple-files": "Hold CTRL (or CMD) to select multiple files",
"enter-an-https-url": "Enter an HTTPS URL",
"lucy-g": "Lucy G",
"flaticon": "Flaticon",
"creative-commons-by-3-0": "Creative Commons BY 3.0",
"gregor-cresnar": "Gregor Cresnar",
"add-this-video-to-any-remote-scene-1-": "Add this Video to any remote '&scene=1'",
"forward-user-to-another-room-they-can-always-return-": "Forward user to another room. They can always return.",
"start-recording-this-stream-experimental-views": "Start Recording this stream. *experimental*' views",
"force-the-user-to-disconnect-they-can-always-reconnect-": "Force the user to Disconnect. They can always reconnect.",
"change-this-audio-s-volume-in-all-remote-scene-views": "Change this Audio's volume in all remote '&scene' views",
"remotely-mute-this-audio-in-all-remote-scene-views": "Remotely Mute this Audio in all remote '&scene' views",
"disable-video-preview": "Disable Video Preview",
"low-quality-preview": "Low-Quality Preview",
"high-quality-preview": "High-Quality Preview",
"send-direct-message": "Send Direct Message",
"advanced-settings-and-remote-control": "Advanced Settings and Remote Control",
"toggle-voice-chat-with-this-guest": "Toggle Voice Chat with this Guest",
"join-by-room-name-here": "Enter a room name to quick join",
"join-room": "Join room",
"share-a-screen-with-others": "Share a Screen with others",
"alert-the-host-you-want-to-speak": "Alert the host you want to speak",
"record-your-stream-to-disk": "Record your stream to disk",
"cancel-the-director-s-video-audio": "Cancel the Director's Video/Audio",
"submit-any-error-logs": "Submit any error logs",
"add-group-chat-to-obs": "Add Group Chat to OBS",
"for-large-group-rooms-this-option-can-reduce-the-load-on-remote-guests-substantially": "For large group rooms, this option can reduce the load on remote guests substantially",
"which-video-codec-would-you-want-used-by-default-": "Which video codec would you want used by default?",
"you-ll-enter-as-the-room-s-director": "You'll enter as the room's director",
"add-your-camera-to-obs": "Add your Camera to OBS",
"remote-screenshare-into-obs": "Remote Screenshare into OBS",
"create-reusable-invite": "Create Reusable Invite",
"encode-the-url-so-that-it-s-harder-for-a-guest-to-modify-the-settings-": "Encode the URL so that it's harder for a guest to modify the settings.",
"more-options": "More Options",
"youtube-video-demoing-how-to-do-this": "Youtube Video demoing how to do this",
"invite-a-guest-or-camera-source-to-publish-into-the-group-room": "Invite a guest or camera source to publish into the group room",
"if-enabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If enabled, the invited guest will not be able to see or hear anyone in the room.",
"use-this-link-in-the-obs-browser-source-to-capture-the-video-or-audio": "Use this link in the OBS Browser Source to capture the video or audio",
"if-enabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If enabled, you must manually add a video to a scene for it to appear.",
"disables-echo-cancellation-and-improves-audio-quality": "Disables Echo Cancellation and improves audio quality",
"audio-only-sources-are-visually-hidden-from-scenes": "Audio-only sources are visually hidden from scenes",
"guest-will-be-prompted-to-enter-a-display-name": "Guest will be prompted to enter a Display Name",
"display-names-will-be-shown-in-the-bottom-left-corner-of-videos": "Display Names will be shown in the bottom-left corner of videos",
"request-1080p60-from-the-guest-instead-of-720p60-if-possible": "Request 1080p60 from the Guest instead of 720p60, if possible",
"the-default-microphone-will-be-pre-selected-for-the-guest": "The default microphone will be pre-selected for the guest",
"the-default-camera-device-will-selected-automatically": "The default camera device will selected automatically",
"the-guest-won-t-have-access-to-changing-camera-settings-or-screenshare": "The guest won't have access to changing camera settings or screenshare",
"the-guest-will-not-see-their-own-self-preview-after-joining": "The guest will not see their own self-preview after joining",
"guests-will-have-an-option-to-poke-the-director-by-pressing-a-button": "Guests will have an option to poke the Director by pressing a button",
"add-an-audio-compressor-to-the-guest-s-microphone": "Add an audio compressor to the guest's microphone",
"add-an-equalizer-to-the-guest-s-microphone-that-the-director-can-control": "Add an Equalizer to the guest's microphone that the director can control",
"the-guest-can-only-see-the-director-s-video-if-provided": "The guest can only see the Director's video, if provided",
"the-guest-s-microphone-will-be-muted-on-joining-they-can-unmute-themselves-": "The guest's microphone will be muted on joining. They can unmute themselves.",
"have-the-guest-join-muted-so-only-the-director-can-unmute-the-guest-": "Have the guest join muted, so only the director can Unmute the guest.",
"make-the-invite-url-encoded-so-parameters-are-harder-to-tinker-with-by-guests": "Make the invite URL encoded, so parameters are harder to tinker with by guests",
"move-the-user-to-another-room-controlled-by-another-director": "Move the user to another room, controlled by another director",
"send-a-direct-message-to-this-user-": "Send a Direct Message to this user.",
"remotely-change-the-volume-of-this-guest": "Remotely change the volume of this guest",
"mute-this-guest-everywhere": "Mute this guest everywhere",
"start-recording-this-remote-stream-to-this-local-drive-experimental-": "Start Recording this remote stream to this local drive. *experimental*'",
"the-remote-guest-will-record-their-local-stream-to-their-local-drive-experimental-": "The Remote Guest will record their local stream to their local drive. *experimental*",
"shift-this-video-down-in-order": "Shift this Video Down in Order",
"current-index-order-of-this-video": "Current Index Order of this Video",
"shift-this-video-up-in-order": "Shift this Video Up in Order",
"remote-audio-settings": "Remote Audio Settings",
"advanced-video-settings": "Advanced Video Settings",
"activate-or-reload-this-video-device-": "Activate or Reload this video device.",
"load-the-next-guest-in-queue": "Load the next guest in queue",
"create-a-secondary-stream": "Create a Secondary Stream",
"add-to-calendar": "Add to Calendar",
"the-director-will-be-visible-in-scenes-as-if-a-performer-themselves-": "The director will be visible in scenes, as if a performer themselves.",
"useful-if-you-want-to-perform-and-direct-at-the-same-time": "Useful if you want to perform and direct at the same time",
"start-streaming": "start streaming",
"if-disabled-the-invited-guest-will-not-be-able-to-see-or-hear-anyone-in-the-room-": "If disabled, the invited guest will not be able to see or hear anyone in the room.",
"if-disabled-you-must-manually-add-a-video-to-a-scene-for-it-to-appear-": "If disabled, you must manually add a video to a scene for it to appear.",
"guests-not-actively-speaking-will-be-hidden": "Guests not actively speaking will be hidden",
"increase-video-quality-that-guests-in-room-see-": "Increase video quality that guests in room see.",
"the-guest-will-not-be-asked-for-a-video-device-on-connection": "The guest will not be asked for a video device on connection",
"toggle-solo-voice-chat": "Toggle Solo Voice Chat",
"add-to-scene-2": "Add to Scene 2",
"add-to-scene-3": "Add to Scene 3",
"add-to-scene-4": "Add to Scene 4",
"add-to-scene-5": "Add to Scene 5",
"add-to-scene-6": "Add to Scene 6",
"add-to-scene-7": "Add to Scene 7",
"hide-this-guest-everywhere": "Hide this guest everywhere",
"set-to-default-audio-channel": "Set to Default Audio Channel",
"set-to-audio-channel-1": "Set to Audio Channel 1",
"set-to-audio-channel-2": "Set to Audio Channel 2",
"set-to-audio-channel-3": "Set to Audio Channel 3",
"set-to-audio-channel-4": "Set to Audio Channel 4",
"set-to-audio-channel-5": "Set to Audio Channel 5",
"toggle-the-remote-guest-s-speaker-output": "Toggle the remote guest's speaker output",
"toggle-the-remote-guest-s-display-output": "Toggle the remote guest's display output",
"set-to-audio-channel-6": "Set to Audio Channel 6",
"set-to-audio-channel-7": "Set to Audio Channel 7",
"set-to-audio-channel-8": "Set to Audio Channel 8",
"force-the-remote-sender-to-issue-a-keyframe-to-all-scenes-fixing-pixel-smearing-issues-": "Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues.",
"remotely-reload-the-guest-s-page-with-a-new-url": "Remotely reload the guest's page with a new URL",
"change-user-parameters": "Change user parameters",
"solo-this-video-everywhere": "Solo this video everywhere",
"request-the-statistics-of-this-video-in-any-active-scene": "Request the statistics of this video in any active scene",
"cannot-see-videos": "Cannot see videos",
"cannot-hear-others": "Cannot hear others",
"see-director-only": "See director only",
"show-mini-preview": "Show Mini preview",
"raise-hand-button": "Raise hand button",
"show-labels": "Show labels",
"transfer-to-a-new-room": "Transfer to a new Room",
"enable-custom-password": "Enable custom password",
"improve-performance-and-quality-with-this-tip": "Improve performance and quality with this tip",
"allow-the-guests-to-pick-a-virtual-backscreen-effect": "Allow the guests to pick a virtual backscreen effect",
"this-low-fi-video-codec-uses-very-little-cpu-even-with-dozens-of-active-viewers-": "This low-fi video codec uses very little CPU, even with dozens of active viewers.",
"the-active-speakers-are-made-visible-automatically": "The active speakers are made visible automatically",
"add-this-video-to-any-remote-scene-2-": "Add this Video to any remote '&scene=2'",
"add-to-scene-8": "Add to Scene 8",
"share-a-website-as-an-embedded-iframe": "Share a website as an embedded iFRAME",
"room-settings": "Room Settings",
"your-audio-and-video-settings": "Your audio and video Settings",
"you-can-also-enable-the-director-s-video-output-afterwards-by-clicking-the-setting-s-button": "You can also enable the director`s Video Output afterwards by clicking the Setting`s button",
"allow-for-remote-stat-monitoring-via-the-monitoring-tool": "Allow for remote stat monitoring via the monitoring tool",
"the-guest-will-be-asked-if-they-want-to-reload-the-previous-link-when-revisiting": "The guest will be asked if they want to reload the previous link when revisiting",
"the-guest-s-self-video-preview-will-appear-tiny-in-the-top-right": "The guest's self-video preview will appear tiny in the top right",
"videos-use-an-animated-transition-when-being-remixed": "Videos use an animated transition when being remixed",
"show-some-prep-suggestions-to-the-guests-on-connect": "Show some prep suggestions to the guests on connect",
"set-the-background-color-to-bright-green": "Set the background color to bright green",
"fade-videos-in-over-500ms": "Fade videos in over 500ms",
"add-a-10px-margin-around-all-video-elements": "Add a 10px margin around all video elements",
"playback-the-video-with-mono-channel-audio": "Playback the video with mono-channel audio",
"have-the-videos-fit-their-respective-areas-even-if-it-means-cropping-a-bit": "Have the videos fit their respective areas, even if it means cropping a bit",
"have-videos-be-aligned-with-sizing-designed-for-vertical-video": "Have videos be aligned with sizing designed for vertical video",
"copy-this-stream-id-to-the-clipboard": "Copy this Stream ID to the clipboard",
"click-here-to-edit-the-label-for-this-stream-changes-will-propagate-to-all-viewers-of-this-stream": "Click here to edit the label for this stream. Changes will propagate to all viewers of this stream",
"this-will-ask-the-remote-guest-for-permission-to-change": "This will ask the remote guest for permission to change",
"a-direct-solo-view-of-the-video-audio-stream-with-nothing-else-its-audio-can-be-remotely-controlled-from-here": "A direct solo view of the video/audio stream with nothing else. Its audio can be remotely controlled from here",
"this-guest-raised-their-hand-click-this-to-clear-notification-": "This guest raised their hand. Click this to clear notification.",
"increase-this-at-your-peril-changes-the-total-inbound-video-bitrate-per-guest-mobile-devices-excluded-webp-mode-also-excluded-": "Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded.",
"hide-this-window": "Hide this window",
"cycle-the-cameras": "Cycle the Cameras",
"have-screen-shares-stream-id-s-use-a-predictable-prefixed-value-instead-of-a-random-one-": "Have screen-shares stream ID's use a predictable prefixed value instead of a random one.",
"toggle-solo-voice-chat-or-hold-ctrl-cmd-when-selecting-to-make-it-two-way-private-": "Toggle solo voice chat or hold CTRL/CMD when selecting to make it two-way private.",
"transfer-any-file-to-the-group": "Transfer any file to the group",
"transfer-any-file": "Transfer any file",
"the-camera-will-load-in-a-default-safe-mode-that-may-work-if-other-modes-fail-": "The camera will load in a default safe-mode that may work if other modes fail.",
"disable-animated-transitions-during-video-mixing": "Disable animated transitions during video mixing",
"allow-the-guest-to-select-a-file-to-upload-to-the-director-once-shared-it-will-show-in-the-chat-as-a-download-link-": "Allow the guest to select a file to upload to the director. Once shared, it will show in the chat as a download link.",
"set-a-countdown-timer-that-this-guest-sees": "Set a countdown timer that this guest sees",
"enter-a-room-name-to-quick-join": "Enter a room name to quick join"
},
"innerHTML": {
"logo-header": "<font id=\"qos\" style=\"color: white;\">O</font>BS.Ninja (RU)",
"copy-this-url": "Скопируйте этот URL-адрес в OBS \"Браузер\"",
"you-are-in-the-control-center": "Вы находитесь в центре управления комнатой",
"joining-room": "Вы присоединяетесь к комнате",
"add-group-chat": "Добавить групповой чат в OBS",
"rooms-allow-for": "В комнатах предусмотрены упрощенный групповой чат и расширенное управление несколькими потоками одновременно.",
"room-name": "Название комнаты",
"password-input-field": "Пароль",
"enter-the-rooms-control": "Войдите в центр управления комнатой",
"show-tips": "Покажите мне несколько советов..",
"added-notes": "\n\t\t\t\t<u><i>Добавленные заметки:</i></u>\n\t\t\t\t<li>Любой может войти в комнату, если знает имя, поэтому оставьте его уникальным</li>\n\t\t\t\t<li>Наличие более четырех (4) человек в комнате не рекомендуется по причинам производительности, но это зависит от вашего оборудования.</li>\n\t\t\t\t<li>Устройства iOS ограничены размерами группы не более двух (2) человек. Это аппаратное ограничение.</li>\n\t\t\t\t",
"back": "Назад",
"add-your-camera": "Добавить свою камеру в OBS",
"ask-for-permissions": "Allow Access to Camera/Microphone",
"waiting-for-camera": "Ожидание загрузки камеры",
"video-source": "Источники видео",
"max-resolution": "Максимальное разрешение",
"balanced": "Сбалансированный",
"smooth-cool": "Гладко и круто",
"select-audio-source": "Выберите источники звука",
"no-audio": "Нет звука",
"select-output-source": " Назначение аудиовыхода: \n\t\t\t\t\t",
"remote-screenshare-obs": "Удаленная демонстрация экрана в OBS",
"note-share-audio": "\n\t\t\t\t\tFirefox не поддерживает обмен аудио",
"select-screen-to-share": "Выберите экран, чтобы поделиться",
"audio-sources": "Источники звука",
"create-reusable-invite": "Создать многоразовое приглашение",
"here-you-can-pre-generate": "Здесь вы можете предварительно сгенерировать повторно используемую ссылку на источник браузера и связанную гостевую ссылку для приглашения..",
"generate-invite-link": "СГЕНЕРИРОВАТЬ ССЫЛКУ-ПРИГЛАШЕНИЕ",
"advanced-paramaters": "Расширенные параметры",
"unlock-video-bitrate": "Разблокировать битрейт видео (20 Мбит/с)",
"force-vp9-video-codec": "Видеокодек Force VP9 (меньше артефактов)",
"enable-stereo-and-pro": "Включить стерео и Pro HD Audio",
"video-resolution": "Разрешение видео: ",
"hide-mic-selection": "Force Default Microphone",
"hide-screen-share": "Скрыть параметр демонстрации экрана",
"allow-remote-control": "Remote Control Camera Zoom (android)",
"add-a-password-to-stream": " Добавить пароль:",
"add-the-guest-to-a-room": " Добавить гостя в комнату:",
"invite-group-chat-type": "В этой комнате гость может:",
"can-see-and-hear": "Видеть и слышать групповой чат",
"can-hear-only": "Только слышать груповой чат",
"cant-see-or-hear": "Не слышать и не видеть групповой чат",
"share-local-video-file": "Stream Media File",
"share-website-iframe": "Share Website",
"run-a-speed-test": "Run a Speed Test",
"read-the-guides": "Browse the Guides",
"info-blob": "\n\t\t\t\t\t\t<h2>Что такое OBS.Ninja</h2><br>\n\t\t\t\t\t\t<li><b>бесплатно</b> на 100%; нет загрузок; нет сбора личных данных; нет входа</li>\n\t\t\t\t\t\t<li>Добавляйте видео со своего смартфона, ноутбука, компьютера или друзей прямо в видеопоток OBS</li>\n\t\t\t\t\t\t<li>Мы используем передовую технологию переадресации Peer-to-Peer, которая обеспечивает конфиденциальность и сверхнизкую задержку</li>\n\t\t\t\t\t\t",
"add-to-scene": "Add to Scene",
"forward-to-room": "Transfer",
"record": "Запись",
"disconnect-guest": "Hangup",
"mute": "Отключить звук",
"change-to-low-quality": "&nbsp;&nbsp;<i class=\"las la-video-slash\"></i>",
"change-to-medium-quality": "&nbsp;&nbsp;<i class=\"las la-video\"></i>",
"change-to-high-quality": "&nbsp;&nbsp;<i class=\"las la-binoculars\"></i>",
"send-direct-chat": "<i class=\"las la-envelope\"></i> Message",
"advanced-camera-settings": "<i class=\"las la-cog\"></i> Advanced",
"voice-chat": "<i class=\"las la-microphone\"></i> Voice Chat",
"open-in-new-tab": "Открыть в новой вкладке",
"copy-to-clipboard": "Скопировано в буфер обмена",
"click-for-quick-room-overview": "❔ Нажмите здесь, чтобы ознакомиться с кратким обзором",
"push-to-talk-enable": "🔊 Включить режим «Нажми, чтобы говорить»‎",
"welcome-to-control-room": "Welcome. This is the control-room for the group-chat. There are different things you can use this room for:<br><br>\t<li>You can host a group chat with friends using a room. Share the blue link to invite guests who will join the chat automatically.</li>\t<li>A group room can handle around 4 to 30 guests, depending on numerous factors, including CPU and available bandwidth of all guests in the room.</li>\t<li>Solo-views of each video are offered under videos as they load. These can be used within an OBS Browser Source.</li>\t<li>You can use the auto-mixing Group Scene, the green link, to auto arrange multiple videos for you in OBS.</li>\t<li>You can use this control room to record isolated video or audio streams, but it is an experimental feature still.</li>\t<li>Videos in the Director's room will be of low quality on purpose; to save bandwidth/CPU</li>\t<li>Guest's in the room will see each other's videos at a very limited quality to conserve bandwidth/CPU.</li>\t<li>OBS will see a guest's video in high-quality; the default video bitrate is 2500kbps.</li>\t<br>\tAs guests join, their videos will appear below. You can bring their video streams into OBS as solo-scenes or you can add them to the Group Scene.\t<br>The Group Scene auto-mixes videos that have been added to the group scene. Please note that the Auto-Mixer requires guests be manually added to it for them to appear in it; they are not added automatically.<br><br>Apple mobile devices, such as iPhones and iPads, do not fully support Video Group Chat. This is a hardware constraint.<br><br>\tFor advanced options and parameters, <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">see the Wiki.</a>",
"more-than-four-can-join": "These four guest slots are just for demonstration. More than four guests can actually join a room.",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tДобро пожаловать в OBS.Ninja! You can send text messages directly to connected peers from here.\n\t\t\t\t",
"names-and-labels-coming-soon": "\n\t\t\t\t\tNames identifying connected peers will be a feature in an upcoming release.\n\t\t\t\t",
"send-chat": "Отправить",
"available-languages": "Доступные языки:",
"add-more-here": "Добавить больше!",
"waiting-for-camera-to-load": "waiting-for-camera-to-load",
"start": "START",
"share-your-mic": "Share your microphone",
"share-your-camera": "Share your Camera",
"share-your-screen": "Share your Screen",
"join-room-with-mic": "Join room with Microphone",
"share-screen-with-room": "Share-screen with Room",
"join-room-with-camera": "Join room with Camera",
"click-start-to-join": "Click Start to Join",
"guests-only-see-director": "Guests can only see the Director's Video",
"default-codec-select": "Preferred Video Codec: ",
"obfuscate_url": "Obfuscate the Invite URL",
"hide-the-links": " LINKS (GUEST INVITES &amp; SCENES)",
"invite-users-to-join": "Guests can use the link to join the group room",
"this-is-obs-browser-source-link": "Use in OBS or other studio software to capture the group video mix",
"mute-scene": "mute in scene",
"mute-guest": "mute guest",
"record-local": " Record Local",
"record-remote": " Record Remote",
"order-down": "<i class=\"las la-minus\"></i>",
"order-up": "<i class=\"las la-plus\"></i>",
"advanced-audio-settings": "<i class=\"las la-sliders-h\"></i> Audio Settings",
"scenes-can-see-director": "Director will also be a performer",
"select-digital-effect": " Digital Video Effects: ",
"add-a-password": " Add a Password:",
"hide-guest": "hide guest",
"toggle-remote-speaker": "Deafen Guest",
"toggle-remote-display": "Blind Guest",
"force-keyframe": "Rainbow Puke",
"change-url": "Change URL",
"change-params": "URL Params",
"solo-video": "Highlight guest",
"stats-remote": " Scene Stats",
"apply-new-guest-settings": "Apply settings",
"cancel": "Cancel",
"add-to-calendar": "Add details to your Calendar:",
"no-effects-applied": "No effects applied",
"blurred-background": "Blurred background",
"digital-greenscreen": "Digital greenscreen",
"virtual-background": "Virtual background",
"use-chrome-instead": "Consider using a Chromium-based browser instead.<br>\n \t\t\t\t\t\tSafari is more prone to having audio issues",
"select-the-video-files-to-share": "SELECT THE VIDEO FILES TO SHARE",
"enter-the-website-URL-you-wish-to-share": "Enter the URL website you wish to share.",
"click-here-for-help": "Click Here for a quick overview and help",
"guests-hear-others": "Guests hear others",
"capture-a-group-scene": "CAPTURE A GROUP SCENE",
"auto-add-guests": "Auto-add guests",
"pro-audio-mode": "Pro-audio mode",
"hide-audio-only-sources": "Hide audio-only sources",
"ask-for-display-name": "Ask for display name",
"show-display-names": "Show display names",
"show-active-speaker": "Show active speakers",
"auto-select-microphone": "Auto-select default microphone",
"auto-select-camera": "Auto-select default camera",
"hide-setting-buttons": "Hide settings button",
"mini-self-preview": "Mini self-preview",
"virtual-backgrounds": "Virtual backgrounds",
"powerful-computers-only": "Only use with powerful computers and small groups!!",
"guests-see-HD-video": "Guests see HD video",
"no-self-preview": "Disable self-preview",
"raise-hand-button": "Display 'raise-hand' button",
"enable-compressor": "Enable audio compressor",
"enable-equalizer": "Enable equalizer as option",
"low-cpu=broadcast-codec": "Low-CPU broadcast codec",
"only-see-director-feed": "Only see the director's feed",
"mute-microphone-by-default": "Mute microphone by default",
"guest-joins-with-no-camera": "Guest joins with no camera",
"unmute-by-director-only": "Unmute by director only",
"obfuscate-link": "Obfuscate link and parameters",
"this-can-reduce-packet-loss": "This can reduce video corruption caused by packet loss",
"use-h264-codec": "Use H264 codec",
"show-active-speakers": "Show active speakers",
"force-mono-audio": "Force mono audio",
"learn-more-about-params": "Learn more about URL parameters at ",
"More-scene-options": "More scene options",
"additional-controls": "Additional controls",
"select-local-image": "Select Local Image",
"close-settings": "Close Settings",
"advanced": "Advanced ",
"invisible-guests": "Not Visible",
"add-to-google-calendar": "Add to Google Calendar",
"add-to-outlook-calendar": "Add to Outlook Calendar",
"add-to-yahoo-calendar": "Add to Yahoo Calendar",
"remote-monitoring": "Remote Monitoring",
"invite-saved-to-cookie": "Invite saved to cookie",
"fade-videos-in": "Fade videos in",
"show-guest-tips": "Show guest setup tips",
"green-background": "Green background",
"add-margin": "Add margin to videos",
"fill-video-space": "Crop video to fit",
"vertical-aspect-ratio": "Vertical video mode",
"add-to-scene2": "add to scene 2",
"user-raised-hand": "Lower Raised Hand",
"unmute": "un-mute",
"unhide-guest": "un-hide",
"undeafen": "un-deafen",
"unblind": "un-blind",
"close": "close",
"send-message": "send message<s pan=\"\"> </s>",
"record-director-local": " Record",
"only-director-can-hear-you": "Only the director can hear you currently.",
"director-muted-you": "The director has muted you.",
"application-audio-capture": "For application-specific audio capture, <a href=\"https://docs.vdo.ninja/audio\" style=\"color: #007AC8;\">see here</a>",
"animate-mixing": "Animate mixing",
"prefix-screenshare": "Prefix screenshare IDs",
"privacy-disabled": "Privacy warning: The director will be able to remotely access your camera and microphone if you continue.",
"face-mesh": "Face mesh (slow load)",
"anonymous-mask": "Anonymous mask",
"dog-face": "Dog ears and nose",
"compatibility-mode": "Compatibility mode",
"disable-animated-mixing": "Disable animations",
"request-upload": " Request File",
"create-timer": "Create Timer",
"edit-url": "Edit URL manually",
"mirror-video": "Mirror",
"toggle-control-video": "Toggle control bar",
"picture-in-picture": "Picture-in-picture",
"chrome-cast": "Cast..",
"join-room": "Join room",
"waiting-for-mic-to-load": "Waiting for mic to load"
},
"placeholders": {
"join-by-room-name-here": "Join by Room Name here",
"enter-a-room-name-here": "Enter a Room Name here",
"optional-room-password-here": "Optional room password here",
"give-this-media-source-a-name-optional-": "Give this media source a name (optional)",
"add-an-optional-password": "Add an optional password",
"enter-room-name-here": "Enter Room name here",
"enter-chat-message-to-send-here": "Enter chat message to send here",
"optional": "optional",
"enter-the-room-name-here": "Enter the room name here",
"enter-the-room-password-here": "Enter the room password here",
"enter-your-message-here": "Enter your message here"
},
"miscellaneous": {
"start": "START",
"new-display-name": "Enter a new Display Name for this stream",
"submit-error-report": "Press OK to submit any error logs to VDO.Ninja. Error logs may contain private information.",
"director-redirect-1": "The director wishes to redirect you to the URL: ",
"director-redirect-2": "\n\nPress OK to be redirected.",
"add-a-label": "Add a label",
"audio-processing-disabled": "Audio processing is disabled with this guest. Can't mute or change volume",
"not-the-director": "<font color='red'>You are not the director of this room. You will have limited to no control. You can try claiming the room after the first director leaves.</font>",
"room-is-claimed": "The room is already claimed by someone else.\n\nOnly the first person to join a room is the assigned director.\n\nRefresh after the first director leaves to claim.",
"room-is-claimed-codirector": "The room is already claimed by someone else.\n\nTrying to join as a co-director...",
"streamid-already-published": "The stream ID you are publishing to is already in use.\n\nPlease try with a different invite link or refresh to retry again.\n\nYou will now be disconnected.",
"director": "Director",
"unknown-user": "Unknown User",
"room-test-not-good": "The room name 'test' is very commonly used and may not be secure.\n\nAre you sure you wish to proceed?",
"load-previous-session": "Would you like to load your previous session's settings?",
"enter-password": "Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"enter-password-2": "Please enter the password below: \n\n(Note: Passwords are case-sensitive.)",
"enter-director-password": "Please enter the director's password:\n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)",
"password-incorrect": "The password was incorrect.\n\nRefresh and try again.",
"enter-display-name": "Please enter your display name:",
"enter-new-display-name": "Enter a new Display Name for this stream",
"what-bitrate": "What bitrate would you like to record at? (kbps)",
"enter-website": "Enter a website URL to share",
"press-ok-to-record": "Press OK to start recording. Press again to stop and download.\n\nWarning: Keep this browser tab active to continue recording.\n\nYou can change the default video bitrate if desired below (kbps)",
"no-streamID-provided": "No streamID was provided; one will be generated randomily.\n\nStream ID: ",
"alphanumeric-only": "Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore",
"stream-id-too-long": "The Stream ID should be less than 45 alPhaNuMeric characters long.\n\nWe will trim it to length.",
"share-with-trusted": "Share only with those you trust",
"pass-recommended": "A password is recommended",
"insecure-room-name": "Insecure room name.",
"allowed-chars": "Allowed chars",
"transfer": "transfer",
"armed": "armed",
"transfer-guest-to-room": "Transfer guests to room:\n\n(Please note rooms must share the same password)",
"transfer-guest-to-url": "Transfer guests to new website URL.\n\n(Guests will be prompted to accept)",
"change-url": "change URL",
"mute-in-scene": "mute in scene",
"unmute-guest": "un-mute guest",
"undeafen": "un-deafen",
"deafen": "deafen guest",
"unblind": "un-blind",
"blind": "blind guest",
"unmute": "un-mute",
"mute-guest": "mute guest",
"unhide": "unhide guest",
"hide-guest": "hide guest",
"confirm-disconnect-users": "Are you sure you wish to disconnect these users?",
"confirm-disconnect-user": "Are you sure you wish to disconnect this user?"
}
}

View File

@ -1,3 +1,20 @@
:root {
--background-color: #141926;
--container-color: #373737;
--button-color: #2A2A2A;
--blue-accent: #4a4c63;
--red-accent: #553737;
--green-accent: #3f4f50;
--olive-accent: #535D32;
--regular-margin: 10px;
--director-margin: 15px 20px 0 0;
--fit-style: contain;
--fadein-speed: 0;
--video-margin: 0px;
--video-rounded: 0px;
--button-radius: 2px;
}
body {
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
@ -79,6 +96,7 @@ iframe {
height: 85%;
width: 100%;
flex: 1;
border: 0;
}
#controls {
@ -87,6 +105,9 @@ iframe {
#controls button {
margin: 5px;
padding: 5px 10px;
border: 0;
border-radius: var(--button-radius);
}
#controls button.active {
@ -142,6 +163,7 @@ canvas {
.graph h2, #log h2 {
margin: 0px 20px;
font-size: 1em;
color: #cacaca;
}
.graph > span {
@ -196,9 +218,9 @@ ol {
margin:auto;
}
}
}
#statsdiv {display: none;}
#statsdiv {display: none;}

View File

@ -65,11 +65,16 @@
Testing location: <select name="turnlist" id="turnlist" onchange="reloadTurn();" title="Select an exact location to test against">
<option selected value="">Automatic</option>
<option value="de1">Saarbruecken, Germany</option>
<option value="de2">Frankfurt, Germany</option>
<option value="fr1">Strasbourg, France</option>
<option value="bra1">São Paulo, Brazil</option>
<option value="cae1">Montreal, Canada</option>
<option value="usc1">Chicago, USA</option>
<option value="usw1">Los Angeles, USA</option>
<option value="aus1">Sidney, Australia</option>
<option value="usw1">Los Angeles 1, USA</option>
<option value="usw2">Los Angeles 2, USA</option>
<option value="aus1">Sydney, Australia</option>
<option value="jap1">Tokyo, Japan</option>
<option value="sing1">Singapore</option>
</select>
<br /><br /><br />
</div>

328
thirdparty/jeeliz/JeelizResizer.js vendored Normal file
View File

@ -0,0 +1,328 @@
/*
This helper can help for:
* adjusting the canvas resolution to the good size -> this is crucial to
optimize the code because if the canvas is too large,
there are too much pixels to compute => it will be slow
* to mirror horizontally or not the canvas -> if the front camera is used we
need it flipped (mirror effect), while if the rear camera is used we need it not flipped
* to get the best camera resolution (either above the canvas resolution or closer)
to balance between performance and quality
*/
"use strict";
const JeelizResizer = (function(){
// private vars:
let _domCanvas = null,
_whCanvasPx = null,
_isApplyCSS = false,
_resizeAttemptsCounter = 0,
_overSamplingFactor = 1,
_isFullScreen = false,
_timerFullScreen = null,
_callbackResize = null,
_isInvFullscreenWH = false;
const _cameraResolutions = [ // all resolutions should be in landscape mode
[640,480],
[768,480],
[800,600],
[960,640],
[960,720],
[1024,768],
[1280,720],
[1920, 1080]
];
//private functions
function add_CSStransform(domElement, CSS){
const CSStransform = domElement.style.transform;
if (CSStransform.indexOf(CSS) !== -1) return;
domElement.style.transform = CSS + ' ' + CSStransform;
}
// Compute overlap between 2 rectangles A and B
// characterized by their width and their height in pixels
// the rectangles are centered
// return the ratio (pixels overlaped)/(total pixels)
function compute_overlap(whA, whB){
const aspectRatioA = whA[0] / whA[1];
const aspectRatioB = whB[0] / whB[1]; //higher aspectRatio -> more landscape
var whLandscape, whPortrait;
if (aspectRatioA > aspectRatioB){
whLandscape = whA, whPortrait = whB;
} else {
whLandscape = whB, whPortrait = whA;
}
// The overlapped area will be always a rectangle
const areaOverlap = Math.min(whLandscape[0], whPortrait[0]) * Math.min(whLandscape[1], whPortrait[1]);
var areaTotal;
if (whLandscape[0]>=whPortrait[0] && whLandscape[1]>=whPortrait[1]){ //union is a rectangle
areaTotal = whLandscape[0]*whLandscape[1];
} else if (whPortrait[0]>whLandscape[0] && whPortrait[1]>whLandscape[1]){ //union is a rectangle
areaTotal = whPortrait[0]*whPortrait[1];
} else { //union is a cross
areaTotal = whLandscape[0]*whLandscape[1];
areaTotal += (whPortrait[1]-whLandscape[1])*whPortrait[0];
}
return areaOverlap / areaTotal;
} //end compute_overlap()
function update_sizeCanvas(){
const domRect = _domCanvas.getBoundingClientRect();
apply_sizeCanvas(domRect.width, domRect.height);
}
function apply_sizeCanvas(width, height){
_whCanvasPx = [
Math.round(_overSamplingFactor * width),
Math.round(_overSamplingFactor * height)
];
// set canvas resolution:
_domCanvas.setAttribute('width', _whCanvasPx[0]);
_domCanvas.setAttribute('height', _whCanvasPx[1]);
// canvas display size:
if (_isApplyCSS){
_domCanvas.style.width = width.toString() + 'px';
_domCanvas.style.height = height.toString() + 'px';
}
}
function on_windowResize(){
// avoid to resize too often using a timer
// (it can create weird bug with some browsers)
if (_timerFullScreen){
clearTimeout(_timerFullScreen);
}
_timerFullScreen = setTimeout(resize_fullScreen, 50);
}
function resize_canvasToFullScreen(){
const wh = [window['innerWidth'], window['innerHeight']];
if (_isInvFullscreenWH){
wh.reverse();
}
apply_sizeCanvas(wh[0], wh[1]);
}
function resize_fullScreen(){
resize_canvasToFullScreen();
JEELIZFACEFILTER.resize();
_timerFullScreen = null;
if (_callbackResize) {
_callbackResize();
}
}
// public methods:
const that = {
// return true or false if the device is in portrait or landscape mode
// see https://stackoverflow.com/questions/4917664/detect-viewport-orientation-if-orientation-is-portrait-display-alert-message-ad
is_portrait: function(){
try{
if (window['matchMedia']("(orientation: portrait)")['matches']){
return true;
} else {
return false;
}
} catch(e){
return (window['innerHeight'] > window['innerWidth']);
}
},
// check whether the user is using IOS or not
// see https://stackoverflow.com/questions/9038625/detect-if-device-is-ios
check_isIOS: function(){
const isIOS = /iPad|iPhone|iPod/.test(navigator['userAgent']) && !window['MSStream'];
return isIOS;
},
// Should be called only if IOS was detected
// see https://stackoverflow.com/questions/8348139/detect-ios-version-less-than-5-with-javascript
get_IOSVersion: function(){
const v = (navigator['appVersion']).match(/OS (\d+)_(\d+)_?(\d+)?/);
return (v.length > 2) ? [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)] : [0, 0, 0];
},
// Check whether the user is using Android or not
// see https://stackoverflow.com/questions/6031412/detect-android-phone-via-javascript-jquery
check_isAndroid: function(){
const ua = navigator['userAgent'].toLowerCase();
return (ua.indexOf('android') !== -1);
},
// Should be called only if Android was detected
// see https://stackoverflow.com/questions/7184573/pick-up-the-android-version-in-the-browser-by-javascript
get_androidVersion: function(){
const ua = navigator['userAgent'].toLowerCase();
const match = ua.match(/android\s([0-9\.]*)/i);
if (!match || match.length<2){
return [0,0,0];
}
const v = match[1].split('.');
return [
parseInt(v[0], 10),
parseInt(v[1], 10),
parseInt(v[2] || 0, 10)
];
},
// to get a video of 480x640 (480 width and 640 height)
// with a mobile phone in portrait mode, the default implementation
// should require a 480x640 video (Chrome, Firefox)
// but bad implementations needs to always request landscape resolutions (so 640x480)
// see https://github.com/jeeliz/jeelizFaceFilter/issues/144
require_flipVideoWHIfPortrait: function(){
// disabled because of https://github.com/jeeliz/jeelizFaceFilter/issues/144
// seems quite a mess though...
/* if (that.check_isIOS()){
//the user is using IOS
const version = that.get_IOSVersion();
if (version[0] >= 13){
if (version[1] <= 1 // IOS 13.0.X
|| (version[1] === 1 && version[2] < 3)){ // IOS 13.1.X with X<3
return false;
}
}
}
if (that.check_isAndroid()){
const version = that.get_androidVersion();
if (version[0] >= 9){ // Android 9+
return false;
}
} */
// normal implementation
return false;
},
// size canvas to the right resolution
// should be called after the page loading
// when the canvas has already the right size
// options:
// - <string> canvasId: id of the canvas
// - <HTMLCanvasElement> canvas: if canvasId is not provided
// - <function> callback: function to launch if there was an error or not
// - <float> overSamplingFactor: facultative. If 1, same resolution than displayed size (default).
// If 2, resolution twice higher than real size
// - <boolean> CSSFlipX: if we should flip the canvas or not. Default: false
// - <boolean> isFullScreen: if we should set the canvas fullscreen. Default: false
// - <function> onResize: function called when the window is resized. Only enabled if isFullScreen = true
// - <boolean> isInvWH: if we should invert width and height for fullscreen mode only. default = false
// - <boolean> isApplyCSS: if we should also apply canvas dimensions as CSS. default = false
size_canvas: function(optionsArg){
const options = Object.assign({
canvasId: 'undefinedCanvasId',
canvas: null,
overSamplingFactor: window.devicePixelRatio || 1,
isFullScreen: false,
isInvWH: false,
CSSFlipX: false,
isApplyCSS: false,
onResize: null,
callback: function(){}
}, optionsArg);
_domCanvas = (options.canvas) ? options.canvas : document.getElementById(options.canvasId);
_isFullScreen = options.isFullScreen;
_isInvFullscreenWH = options.isInvWH;
_isApplyCSS = options.isApplyCSS;
_overSamplingFactor = options.overSamplingFactor;
if (_isFullScreen){
// we are in fullscreen mode
_callbackResize = options.onResize;
resize_canvasToFullScreen();
window.addEventListener('resize', on_windowResize, false);
window.addEventListener('orientationchange', on_windowResize, false);
} else { // not fullscreen mode
// get display size of the canvas:
const domRect = _domCanvas.getBoundingClientRect();
if (domRect.width===0 || domRect.height===0){
console.log('WARNING in JeelizResize.size_canvas(): the canvas has its width or its height null, Retry a bit later...');
if (++_resizeAttemptsCounter > 20){
options.callback('CANNOT_RESIZECANVAS');
return;
}
setTimeout(that.size_canvas.bind(null, options), 50);
return;
}
// do resize canvas:
_resizeAttemptsCounter = 0;
update_sizeCanvas();
}
// flip horizontally if required:
if (options.CSSFlipX){
add_CSStransform(_domCanvas, 'rotateY(180deg)');
}
// compute the best camera resolutions:
const allResolutions = _cameraResolutions.map(function(x){
return x.slice(0)
});
// if we are in portrait mode, the camera is also in portrait mode
// so we need to set all resolutions to portrait mode
if (that.is_portrait() && that.require_flipVideoWHIfPortrait()){
allResolutions.forEach(function(wh){
wh.reverse();
});
}
// sort camera resolutions from the best to the worst:
allResolutions.sort(function(resA, resB){
return compute_overlap(resB, _whCanvasPx) - compute_overlap(resA, _whCanvasPx);
});
// pick the best camera resolution:
const bestCameraResolution = {
'idealWidth': allResolutions[0][0],
'idealHeight': allResolutions[0][1]
};
console.log('INFO in JeelizResizer: bestCameraResolution =', bestCameraResolution);
// launch the callback function after a small interval to let it
// some time to size:
setTimeout(options.callback.bind(null, false, bestCameraResolution), 1);
}, //end size_canvas()
// Should be called if the canvas is resized to update the canvas resolution:
resize_canvas: function(){
if (_isFullScreen){
resize_canvasToFullScreen()
} else {
update_sizeCanvas();
}
},
get_canvasSize: function(){
return _whCanvasPx;
}
}; //end that
return that;
})();
// Export ES6 module:
try {
module.exports = JeelizResizer;
} catch(e){
console.log('JeelizResizer ES6 Module not exported');
window.JeelizResizer = JeelizResizer;
}

438
thirdparty/jeeliz/JeelizThreeHelper.js vendored Normal file
View File

@ -0,0 +1,438 @@
/*
Helper for Three.js
*/
const JeelizThreeHelper = (function(){
// internal settings:
const _settings = {
rotationOffsetX: 0.0, // negative -> look upper. in radians
pivotOffsetYZ: [0.2, 0.6],// YZ of the distance between the center of the cube and the pivot
detectionThreshold: 0.8, // sensibility, between 0 and 1. Less -> more sensitive
detectionHysteresis: 0.02,
//tweakMoveYRotateX: 0,//0.5, // tweak value: move detection window along Y axis when rotate the face around X (look up <-> down)
cameraMinVideoDimFov: 35 // Field of View for the smallest dimension of the video in degrees
};
// private vars:
let _threeRenderer = null,
_threeScene = null,
_threeVideoMesh = null,
_threeVideoTexture = null,
_threeTranslation = null;
let _maxFaces = -1,
_isMultiFaces = false,
_detectCallback = null,
_isVideoTextureReady = false,
_isSeparateThreeCanvas = false,
_faceFilterCv = null,
_videoElement = null,
_isDetected = false,
_scaleW = 1,
_canvasAspectRatio = -1;
const _threeCompositeObjects = [];
let _gl = null,
_glVideoTexture = null,
_glShpCopyCut = null,
_glShpCopyCutVideoMatUniformPointer = null;
let _videoTransformMat2 = null;
// private funcs:
function destroy(){
_isVideoTextureReady = false;
_threeCompositeObjects.splice(0);
if (_threeVideoTexture){
_threeVideoTexture.dispose();
_threeVideoTexture = null;
}
}
function create_threeCompositeObjects(){
for (let i=0; i<_maxFaces; ++i){
// COMPOSITE OBJECT WHICH WILL TRACK A DETECTED FACE
const threeCompositeObject = new THREE.Object3D();
threeCompositeObject.frustumCulled = false;
threeCompositeObject.visible = false;
_threeCompositeObjects.push(threeCompositeObject);
_threeScene.add(threeCompositeObject);
}
}
function create_videoScreen(){
const videoScreenVertexShaderSource = "attribute vec2 position;\n\
uniform mat2 videoTransformMat2;\n\
varying vec2 vUV;\n\
void main(void){\n\
gl_Position = vec4(position, 0., 1.);\n\
vUV = 0.5 + videoTransformMat2 * position;\n\
}";
const videoScreenFragmentShaderSource = "precision lowp float;\n\
uniform sampler2D samplerVideo;\n\
varying vec2 vUV;\n\
void main(void){\n\
gl_FragColor = texture2D(samplerVideo, vUV);\n\
}";
if (_isSeparateThreeCanvas){
const compile_shader = function(source, type, typeString) {
const glShader = _gl.createShader(type);
_gl.shaderSource(glShader, source);
_gl.compileShader(glShader);
if (!_gl.getShaderParameter(glShader, _gl.COMPILE_STATUS)) {
alert("ERROR IN " + typeString + " SHADER: " + _gl.getShaderInfoLog(glShader));
return null;
}
return glShader;
};
const glShaderVertex = compile_shader(videoScreenVertexShaderSource, _gl.VERTEX_SHADER, 'VERTEX');
const glShaderFragment = compile_shader(videoScreenFragmentShaderSource, _gl.FRAGMENT_SHADER, 'FRAGMENT');
_glShpCopyCut = _gl.createProgram();
_gl.attachShader(_glShpCopyCut, glShaderVertex);
_gl.attachShader(_glShpCopyCut, glShaderFragment);
_gl.linkProgram(_glShpCopyCut);
const samplerVideo = _gl.getUniformLocation(_glShpCopyCut, 'samplerVideo');
_glShpCopyCutVideoMatUniformPointer = _gl.getUniformLocation(_glShpCopyCut, 'videoTransformMat2');
return;
}
// init video texture with red:
_threeVideoTexture = new THREE.DataTexture( new Uint8Array([255,0,0]), 1, 1, THREE.RGBFormat);
_threeVideoTexture.needsUpdate = true;
// CREATE THE VIDEO BACKGROUND:
const videoMaterial = new THREE.RawShaderMaterial({
depthWrite: false,
depthTest: false,
vertexShader: videoScreenVertexShaderSource,
fragmentShader: videoScreenFragmentShaderSource,
uniforms:{
samplerVideo: {value: _threeVideoTexture},
videoTransformMat2: {
value: _videoTransformMat2
}
}
});
const videoGeometry = new THREE.BufferGeometry()
const videoScreenCorners = new Float32Array([-1,-1, 1,-1, 1,1, -1,1]);
videoGeometry.addAttribute( 'position', new THREE.BufferAttribute( videoScreenCorners, 2 ) );
videoGeometry.setIndex(new THREE.BufferAttribute(new Uint16Array([0,1,2, 0,2,3]), 1));
_threeVideoMesh = new THREE.Mesh(videoGeometry, videoMaterial);
that.apply_videoTexture(_threeVideoMesh);
_threeVideoMesh.renderOrder = -1000; // render first
_threeVideoMesh.frustumCulled = false;
_threeScene.add(_threeVideoMesh);
} //end create_videoScreen()
function detect(detectState){
_threeCompositeObjects.forEach(function(threeCompositeObject, i){
_isDetected = threeCompositeObject.visible;
const ds = detectState[i];
if (_isDetected && ds.detected < _settings.detectionThreshold-_settings.detectionHysteresis){
// DETECTION LOST
if (_detectCallback) _detectCallback(i, false);
threeCompositeObject.visible = false;
} else if (!_isDetected && ds.detected > _settings.detectionThreshold+_settings.detectionHysteresis){
// FACE DETECTED
if (_detectCallback) _detectCallback(i, true);
threeCompositeObject.visible = true;
}
}); //end loop on all detection slots
}
function update_poses(ds, threeCamera){
// tan( <horizontal FoV> / 2 ):
const halfTanFOVX = Math.tan(threeCamera.aspect * threeCamera.fov * Math.PI/360); //tan(<horizontal FoV>/2), in radians (threeCamera.fov is vertical FoV)
_threeCompositeObjects.forEach(function(threeCompositeObject, i){
if (!threeCompositeObject.visible) return;
const detectState = ds[i];
// tweak Y position depending on rx:
//const tweak = _settings.tweakMoveYRotateX * Math.tan(detectState.rx);
const cz = Math.cos(detectState.rz), sz = Math.sin(detectState.rz);
// relative width of the detection window (1-> whole width of the detection window):
const W = detectState.s * _scaleW;
// distance between the front face of the cube and the camera:
const DFront = 1 / ( 2 * W * halfTanFOVX );
// D is the distance between the center of the unit cube and the camera:
const D = DFront + 0.5;
// coords in 2D of the center of the detection window in the viewport:
const xv = detectState.x * _scaleW;
const yv = detectState.y * _scaleW;
// coords in 3D of the center of the cube (in the view coordinates system):
const z = -D; // minus because view coordinate system Z goes backward
const x = xv * D * halfTanFOVX;
const y = yv * D * halfTanFOVX / _canvasAspectRatio;
// set position before pivot:
threeCompositeObject.position.set(-sz*_settings.pivotOffsetYZ[0], -cz*_settings.pivotOffsetYZ[0], -_settings.pivotOffsetYZ[1]);
// set rotation and apply it to position:
threeCompositeObject.rotation.set(detectState.rx+_settings.rotationOffsetX, detectState.ry, detectState.rz, "ZYX");
threeCompositeObject.position.applyEuler(threeCompositeObject.rotation);
// add translation part:
_threeTranslation.set(x, y+_settings.pivotOffsetYZ[0], z+_settings.pivotOffsetYZ[1]);
threeCompositeObject.position.add(_threeTranslation);
}); //end loop on composite objects
}
//public methods:
const that = {
// launched with the same spec object than callbackReady. set spec.threeCanvasId to the ID of the threeCanvas to be in 2 canvas mode:
init: function(spec, detectCallback){
destroy();
_maxFaces = spec.maxFacesDetected;
_glVideoTexture = spec.videoTexture;
_videoTransformMat2 = spec.videoTransformMat2;
_gl = spec.GL;
_faceFilterCv = spec.canvasElement;
_isMultiFaces = (_maxFaces>1);
_videoElement = spec.videoElement;
// enable 2 canvas mode if necessary:
let threeCanvas = null;
if (spec.threeCanvasId){
_isSeparateThreeCanvas = true;
// adjust the threejs canvas size to the threejs canvas:
threeCanvas = document.getElementById(spec.threeCanvasId);
threeCanvas.setAttribute('width', _faceFilterCv.width);
threeCanvas.setAttribute('height', _faceFilterCv.height);
} else {
threeCanvas = _faceFilterCv;
}
if (typeof(detectCallback) !== 'undefined'){
_detectCallback = detectCallback;
}
// init THREE.JS context:
_threeRenderer = new THREE.WebGLRenderer({
context: (_isSeparateThreeCanvas) ? null : _gl,
canvas: threeCanvas,
alpha: (_isSeparateThreeCanvas || spec.alpha) ? true : false
});
_threeScene = new THREE.Scene();
_threeTranslation = new THREE.Vector3();
create_threeCompositeObjects();
create_videoScreen();
// handle device orientation change:
window.addEventListener('orientationchange', function(){
setTimeout(JEELIZFACEFILTER.resize, 1000);
}, false);
const returnedDict = {
videoMesh: _threeVideoMesh,
renderer: _threeRenderer,
scene: _threeScene
};
if (_isMultiFaces){
returnedDict.faceObjects = _threeCompositeObjects
} else {
returnedDict.faceObject = _threeCompositeObjects[0];
}
return returnedDict;
}, //end that.init()
detect: function(detectState){
const ds = (_isMultiFaces) ? detectState : [detectState];
// update detection states:
detect(ds);
},
get_isDetected: function() {
return _isDetected;
},
render: function(detectState, threeCamera){
const ds = (_isMultiFaces) ? detectState : [detectState];
// update detection states then poses:
detect(ds);
update_poses(ds, threeCamera);
if (_isSeparateThreeCanvas){
// render the video texture on the faceFilter canvas:
_gl.viewport(0, 0, _faceFilterCv.width, _faceFilterCv.height);
_gl.useProgram(_glShpCopyCut);
_gl.uniformMatrix2fv(_glShpCopyCutVideoMatUniformPointer, false, _videoTransformMat2);
_gl.activeTexture(_gl.TEXTURE0);
_gl.bindTexture(_gl.TEXTURE_2D, _glVideoTexture);
_gl.drawElements(_gl.TRIANGLES, 3, _gl.UNSIGNED_SHORT, 0);
} else {
// reinitialize the state of THREE.JS because JEEFACEFILTER have changed stuffs:
// -> can be VERY costly !
_threeRenderer.state.reset();
}
// trigger the render of the THREE.JS SCENE:
_threeRenderer.render(_threeScene, threeCamera);
},
sortFaces: function(bufferGeometry, axis, isInv){ // sort faces long an axis
// Useful when a bufferGeometry has alpha: we should render the last faces first
const axisOffset = {X:0, Y:1, Z:2}[axis.toUpperCase()];
const sortWay = (isInv) ? -1 : 1;
// fill the faces array:
const nFaces = bufferGeometry.index.count/3;
const faces = new Array(nFaces);
for (let i=0; i<nFaces; ++i){
faces[i] = [bufferGeometry.index.array[3*i], bufferGeometry.index.array[3*i+1], bufferGeometry.index.array[3*i+2]];
}
// compute centroids:
const aPos = bufferGeometry.attributes.position.array;
const centroids = faces.map(function(face, faceIndex){
return [
(aPos[3*face[0]]+aPos[3*face[1]]+aPos[3*face[2]])/3, // X
(aPos[3*face[0]+1]+aPos[3*face[1]+1]+aPos[3*face[2]+1])/3, // Y
(aPos[3*face[0]+2]+aPos[3*face[1]+2]+aPos[3*face[2]+2])/3, // Z
face
];
});
// sort centroids:
centroids.sort(function(ca, cb){
return (ca[axisOffset]-cb[axisOffset]) * sortWay;
});
// reorder bufferGeometry faces:
centroids.forEach(function(centroid, centroidIndex){
const face = centroid[3];
bufferGeometry.index.array[3*centroidIndex] = face[0];
bufferGeometry.index.array[3*centroidIndex+1] = face[1];
bufferGeometry.index.array[3*centroidIndex+2] = face[2];
});
}, //end sortFaces
get_threeVideoTexture: function(){
return _threeVideoTexture;
},
apply_videoTexture: function(threeMesh){
if (_isVideoTextureReady){
return;
}
threeMesh.onAfterRender = function(){
// Replace _threeVideoTexture.__webglTexture by the real video texture:
try {
_threeRenderer.properties.update(_threeVideoTexture, '__webglTexture', _glVideoTexture);
_threeVideoTexture.magFilter = THREE.LinearFilter;
_threeVideoTexture.minFilter = THREE.LinearFilter;
_isVideoTextureReady = true;
} catch(e){
console.log('WARNING in JeelizThreeHelper: the glVideoTexture is not fully initialized');
}
delete(threeMesh.onAfterRender);
};
},
// create an occluder, IE a transparent object which writes on the depth buffer:
create_threejsOccluder: function(occluderURL, callback){
const occluderMesh = new THREE.Mesh();
new THREE.BufferGeometryLoader().load(occluderURL, function(occluderGeometry){
const mat = new THREE.ShaderMaterial({
vertexShader: THREE.ShaderLib.basic.vertexShader,
fragmentShader: "precision lowp float;\n void main(void){\n gl_FragColor=vec4(1.,0.,0.,1.);\n }",
uniforms: THREE.ShaderLib.basic.uniforms,
colorWrite: false
});
occluderMesh.renderOrder = -1; //render first
occluderMesh.material = mat;
occluderMesh.geometry = occluderGeometry;
if (typeof(callback)!=='undefined' && callback) callback(occluderMesh);
});
return occluderMesh;
},
set_pivotOffsetYZ: function(pivotOffset) {
_settings.pivotOffsetYZ = pivotOffset;
},
create_camera: function(zNear, zFar){
const threeCamera = new THREE.PerspectiveCamera(1, 1, (zNear) ? zNear : 0.1, (zFar) ? zFar : 100);
that.update_camera(threeCamera);
return threeCamera;
},
update_camera: function(threeCamera){
// compute aspectRatio:
const canvasElement = _threeRenderer.domElement;
const cvw = canvasElement.width;
const cvh = canvasElement.height;
_canvasAspectRatio = cvw / cvh;
// compute vertical field of view:
const vw = _videoElement.videoWidth;
const vh = _videoElement.videoHeight;
const videoAspectRatio = vw / vh;
const fovFactor = (vh > vw) ? (1.0 / videoAspectRatio) : 1.0;
const fov = _settings.cameraMinVideoDimFov * fovFactor;
console.log('INFO in JeelizThreeHelper - update_camera(): Estimated vertical video FoV is', fov);
// compute X and Y offsets in pixels:
let scale = 1.0;
if (_canvasAspectRatio > videoAspectRatio) {
// the canvas is more in landscape format than the video, so we crop top and bottom margins:
scale = cvw / vw;
} else {
// the canvas is more in portrait format than the video, so we crop right and left margins:
scale = cvh / vh;
}
const cvws = vw * scale, cvhs = vh * scale;
const offsetX = (cvws - cvw) / 2.0;
const offsetY = (cvhs - cvh) / 2.0;
_scaleW = cvw / cvws;
// apply parameters:
threeCamera.aspect = _canvasAspectRatio;
threeCamera.fov = fov;
console.log('INFO in JeelizThreeHelper.update_camera(): camera vertical estimated FoV is', fov, 'deg');
threeCamera.setViewOffset(cvws, cvhs, offsetX, offsetY, cvw, cvh);
threeCamera.updateProjectionMatrix();
// update drawing area:
_threeRenderer.setSize(cvw, cvh, false);
_threeRenderer.setViewport(0, 0, cvw, cvh);
}, //end update_camera()
resize: function(w, h, threeCamera){
_threeRenderer.domElement.width = w;
_threeRenderer.domElement.height = h;
JEELIZFACEFILTER.resize();
if (threeCamera){
that.update_camera(threeCamera);
}
}
}
return that;
})();
window.JeelizThreeHelper = JeelizThreeHelper;

2
thirdparty/jeeliz/Tween.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,151 @@
"use strict";
const _states = {
idle : 0,
loading : 1,
dragging : 2
}
let _state = _states.idle; // MT217 : initialize your state always (even with a loading value)
let _dP = new window.THREE.Vector3();
let _x0 = -1; let _y0 = -1;
let _scenes = null;
let _boundFunction = null;
function updateMeshPosition(canvas, event) {
const MOUSEVECTOR = new window.THREE.Vector3();
const DIRECTIONVECTOR = new window.THREE.Vector3();
const VIEWPORTVECTOR = new window.THREE.Vector3();
const _headCenterZ = -1;
if (_state !== _states.dragging) return; // MT217
const isTouch = !!((event.touches && event.touches.length));// MT217 is touch or mouse event ?
const xPx = (isTouch) ? event.touches[0].clientX : event.clientX; // MT217 : make the distinction between touch and mouse event
const yPx = (isTouch) ? event.touches[0].clientY : event.clientY; // if touch event, consider only the first finger
const dxPx = xPx - _x0; // in pixels
const dyPx = yPx - _y0; // in pixels too
_x0 = xPx;
_y0 = yPx;
// calcul des coo de dxPx, dyPx dans le viewport
// les offsets du canvas s'annulent -> que facteur d'échelle a appliquer
const dx = -dxPx / canvas.offsetWidth;
const dy = -dyPx / canvas.offsetHeight;
// Only check intersects if object is visible
// If there is only 1 object, we don't check intersections too
const mesh = (_scenes.length === 1) ? _scenes[0] : _scenes.find( function(scene){
if (!scene.parent.visible) {
return false;
}
// TODO: Check if a child geometry is an occlusion object. If so remove it from the intersection list
MOUSEVECTOR.set(-(xPx / canvas.offsetWidth) * 2 + 1, -(yPx / canvas.offsetHeight) * 2 + 1, 0.5);
const raycaster = new window.THREE.Raycaster();
raycaster.setFromCamera(MOUSEVECTOR, window.THREECAMERA);
const intersects = raycaster.intersectObjects(scene.children);
return (intersects.length > 0);
});
if (!mesh) {
return;
}
VIEWPORTVECTOR.set(dx, dy, 1);
DIRECTIONVECTOR.copy(VIEWPORTVECTOR);
if (!window.THREECAMERA){
throw new Error('Cannot find the THREE.js camera. Please check that window.THREECAMERA is the default scene camera');
}
DIRECTIONVECTOR.unproject(window.THREECAMERA);
DIRECTIONVECTOR.sub(window.THREECAMERA.position);
DIRECTIONVECTOR.normalize();
// we calculate the coefficient that will allow us to find our mesh's position
const k = _headCenterZ / DIRECTIONVECTOR.z;
// _dP = displacement in the scene (=world) ref :
_dP.copy(DIRECTIONVECTOR).multiplyScalar(k);
_dP.setZ(0); // bcoz we only want to displace in the (0xy) plane
const _quat = new window.THREE.Quaternion();
const _eul = new window.THREE.Euler();
_eul.setFromQuaternion(_quat);
// convert _dP to mesh ref to apply it directly to mesh.position :
// _dP is a vector so apply only the rotation part (not the translation)
_dP.applyEuler(mesh.getWorldQuaternion(_eul));
// Boost movement to follow better the mouse/touch
_dP.multiplyScalar(10);
// apply _dP
mesh.position.add(_dP);
}
function setMousePosition0(event) { // save initial position of the mouse
const isTouch = !!((event.touches && event.touches.length));// MT217 is touch or mouse event ?
if (isTouch && event.touches.length > 1) return; // MT217 if the user put a second finger while dragging
_x0 = (isTouch) ? event.touches[0].clientX : event.clientX; // MT217
_y0 = (isTouch) ? event.touches[0].clientY : event.clientY;
}
function mouseDown(event) {
setMousePosition0(event); // MANTIS201
_state = _states.dragging;
}
function mouseUp() {
_state = _states.idle;
}
function addDragEventListener(scenes, canvasId, remove) {
_scenes = Array.isArray(scenes) ? scenes : [scenes];
const canvas = document.getElementById((typeof(canvasId) === 'undefined') ? 'jeeFaceFilterCanvas' : canvasId);
_state = _states.idle; // MT217 : initialize your state always (even with a loading value)
_dP = new window.THREE.Vector3();
_x0 = undefined; _y0 = undefined;
if (remove) {
// REMOVE OUR LISTENERS
canvas.removeEventListener('mousemove', _boundFunction, true);
canvas.removeEventListener('touchmove', _boundFunction, true);
// BEGINNING OF THE INTERACTION
canvas.removeEventListener('mousedown', mouseDown);
canvas.removeEventListener('touchstart', mouseDown);
// END OF THE INTERACTION
canvas.removeEventListener('mouseup', mouseUp);
canvas.removeEventListener('touchend', mouseUp);
// ALSO END BUT IN CASE LEAVING CANVAS OR ALERT BOX ECT...
canvas.removeEventListener('mouseout', mouseUp);
canvas.removeEventListener('touchcancel', mouseUp);
} else {
// SET OUR LISTENERS
_boundFunction = updateMeshPosition.bind(this, canvas)
canvas.addEventListener('mousemove', _boundFunction, true);
// canvas.addEventListener('touchmove', createTouchEvent, true)
canvas.addEventListener('touchmove', _boundFunction, true); // MT217
// BEGINNING OF THE INTERACTION
canvas.addEventListener('mousedown', mouseDown);
canvas.addEventListener('touchstart', mouseDown);
// END OF THE INTERACTION
canvas.addEventListener('mouseup', mouseUp);
canvas.addEventListener('touchend', mouseUp);
// ALSO END BUT IN CASE LEAVING CANVAS OR ALERT BOX ECT...
canvas.addEventListener('mouseout', mouseUp);
canvas.addEventListener('touchcancel', mouseUp);
}
}

View File

@ -0,0 +1,75 @@
function addVideoRecordingEffect(canvas) {
var viewWidth,
viewHeight,
canvas = document.getElementById("canvasVideoEffect"),
ctx;
// change these settings
var patternSize = 64,
patternScaleX = 3,
patternScaleY = 1,
patternRefreshInterval = 8,
patternAlpha = 25; // int between 0 and 255,
var patternPixelDataLength = patternSize * patternSize * 4,
patternCanvas,
patternCtx,
patternData,
frame = 0;
// create a canvas which will render the grain
function initCanvas() {
viewWidth = canvas.width = canvas.clientWidth;
viewHeight = canvas.height = canvas.clientHeight;
ctx = canvas.getContext('2d');
ctx.scale(patternScaleX, patternScaleY);
}
// create a canvas which will be used as a pattern
function initGrain() {
patternCanvas = document.createElement('canvas');
patternCanvas.width = patternSize;
patternCanvas.height = patternSize;
patternCtx = patternCanvas.getContext('2d');
patternData = patternCtx.createImageData(patternSize, patternSize);
}
// put a random shade of gray into every pixel of the pattern
function update() {
var value;
for (var i = 0; i < patternPixelDataLength; i += 1) {
value = (Math.random() * 155) | 0;
patternData.data[i ] = value;
patternData.data[i + 10] = value;
patternData.data[i + 15] = value;
patternData.data[i + 11] = patternAlpha;
}
patternCtx.putImageData(patternData, 0, 0);
}
// fill the canvas using the pattern
function draw() {
ctx.clearRect(0, 0, viewWidth, viewHeight);
ctx.fillStyle = ctx.createPattern(patternCanvas, 'repeat');
ctx.fillRect(0, 0, viewWidth, viewHeight);
}
function loop() {
if (++frame % patternRefreshInterval === 0) {
update();
draw();
}
requestAnimationFrame(loop);
}
initCanvas();
initGrain();
requestAnimationFrame(loop);
}

View File

@ -0,0 +1,263 @@
/*
Use JEELIZ FACE FILTER API to control the movements of a camera
This script has been put into shared because it can be used with different 3D engines
We have at least 2 integration examples:
- with CesiumJS for a head controlled Google Earth like demo
- with THREE.JS for a camera controller (THREE.HeadControls)
==== INITIALIZATION ====
HeadControls.init(spec) with spec =
spec (*-> mandatory):
- settings: object. override default settings if specified
- canvasId*: id of the canvas where the JEELIZVTO will be initialized. We will draw the face tracking on it
- callbackReady: callback launched when the controller is ready. launched with errCode if error, false otherwise
- callbackMove*: function to move the camera
- disableRestPosition: do not offset the face position with a rest position. Default: false
- NNCPath*: path of the NN net
==== OTHER METHODS ====
HeadControls.toggle(<boolean>onOff): toggle on or off the HeadControls
*/
var HeadControls = (function(){
const _defaultSettings = {
detectionThreshold: 0.85, // sensibility, between 0 and 1. Less -> more sensitive
detectionHysteresis: 0.05,
tol: {
rx: 5,// do not move if head turn more than this value (in degrees) from head rest position
ry: 5,
s: 5 // do not move forward/backward if head is larger/smaller than this percent from the rest position
},
sensibility: {
rx: 1,
ry: 1,
s: 1
}
};
// private variables:
let _settings = null;
const _returnValue = {
dRx:0, dRy: 0,
dZ: 0
};
// internal state:
const _state = {
isLoaded: false,
isDetected:false,
isEnabled: false,
restHeadPosition: { // position of the head matching with No Move
needsUpdate: false,
s: 0,
rx: 0,
ry: 0
}
};
let _lastTimestamp = 0;
let _gl = null, _cv = null, _videoTexture = null, _videoTransformMat2 = null, _glHeadSearchDrawShaderProgram = null;
let _headSearchUniformXys = null, _headSearchUniformVideoTransformMat2 = null;
let _disableRestPosition = false;
// private functions:
function compute_delta(ref, val, tol, sensibility){
if (Math.abs(ref-val)<tol){
return 0;
}
return (val-ref) * sensibility;
}
function compile_shader(source, glType, typeString) {
const glShader = _gl.createShader(glType);
_gl.shaderSource(glShader, source);
_gl.compileShader(glShader);
if (!_gl.getShaderParameter(glShader, _gl.COMPILE_STATUS)) {
alert("ERROR IN " + typeString + " SHADER: " + _gl.getShaderInfoLog(glShader));
return null;
}
return glShader;
};
function init_headSearchDraw(){
// build _glHeadSearchDrawShaderProgram:
const shaderVertexSource = "\n\
attribute vec2 aat_position;\n\
varying vec2 vUV;\n\
\n\
void main(void) {\n\
gl_Position = vec4(aat_position, 0., 1.);\n\
vUV = 0.5 + 0.5 * aat_position;\n\
vUV.x = 1.-vUV.x; // mirror diplay\n\
}";
const shaderFragmentSource = "\n\
precision lowp float;\n\
varying vec2 vUV;\n\
\n\
uniform sampler2D samplerVideo;\n\
uniform mat2 videoTransformMat2;\n\
uniform vec3 uxys;\n\
\n\
void main(void) {\n\
vec2 uvVideoCentered = 2.0 * videoTransformMat2 * (vUV - 0.5);\n\
vec2 uvVideo = uvVideoCentered + 0.5;\n\
vec3 colorVideo = texture2D(samplerVideo, uvVideo).rgb;\n\
vec2 pos = vUV*2.-vec2(1.,1.);\n\
vec2 isInside = step(uxys.xy-uxys.z*vec2(1.,1.), pos);\n\
isInside *= step(pos, uxys.xy+uxys.z*vec2(1.,1.));\n\
vec2 blendCenterFactor = abs(pos-uxys.xy) / uxys.z;\n\
float alpha = isInside.x * isInside.y * pow(max(blendCenterFactor.x, blendCenterFactor.y), 3.);\n\
vec3 color = mix(colorVideo, vec3(0.,0.6,1.), alpha);\n\
gl_FragColor = vec4(color,1.);\n\
}";
const glShaderVertex = compile_shader(shaderVertexSource, _gl.VERTEX_SHADER, 'VERTEX');
const glShaderFragment = compile_shader(shaderFragmentSource, _gl.FRAGMENT_SHADER, 'FRAGMENT');
_glHeadSearchDrawShaderProgram = _gl.createProgram();
_gl.attachShader(_glHeadSearchDrawShaderProgram, glShaderVertex);
_gl.attachShader(_glHeadSearchDrawShaderProgram, glShaderFragment);
_gl.linkProgram(_glHeadSearchDrawShaderProgram);
const samplerVideo = _gl.getUniformLocation(_glHeadSearchDrawShaderProgram, 'samplerVideo');
_headSearchUniformXys = _gl.getUniformLocation(_glHeadSearchDrawShaderProgram, 'uxys');
_headSearchUniformVideoTransformMat2 = _gl.getUniformLocation(_glHeadSearchDrawShaderProgram, 'videoTransformMat2');
_gl.useProgram(_glHeadSearchDrawShaderProgram);
_gl.uniform1i(samplerVideo, 0);
} //end init_headSearchDraw()
function draw_headSearch(detectState){
// unbind the current FBO and set the viewport as the whole canvas:
_gl.viewport(0, 0, _cv.width, _cv.height);
// use the head draw shader program and sync uniforms:
_gl.useProgram(_glHeadSearchDrawShaderProgram);
_gl.activeTexture(_gl.TEXTURE0);
_gl.bindTexture(_gl.TEXTURE_2D, _videoTexture);
_gl.uniform3f(_headSearchUniformXys, detectState.x, detectState.y, detectState.s);
_gl.uniformMatrix2fv(_headSearchUniformVideoTransformMat2, false, _videoTransformMat2);
// draw the square looking for the head
// the VBO filling the whole screen is still bound to the context
// fill the viewPort:
_gl.drawElements(_gl.TRIANGLES, 3, _gl.UNSIGNED_SHORT, 0);
}
function compute_cameraMove(detectState){
if (_state.isDetected && detectState.detected<_settings.detectionThreshold-_settings.detectionHysteresis){
// DETECTION LOST
_state.isDetected = false;
_returnValue.dRx = 0;
_returnValue.dRy = 0;
_returnValue.dZ = 0;
} else if (!_state.isDetected && detectState.detected>_settings.detectionThreshold+_settings.detectionHysteresis){
// FACE DETECTED
_state.isDetected = true;
}
if (_state.isEnabled){
draw_headSearch(detectState);
}
if (!_state.isEnabled || !_state.isDetected || !_state.isLoaded){
return _returnValue; // no camera move
}
if (_state.restHeadPosition.needsUpdate && !_disableRestPosition){
_state.restHeadPosition.needsUpdate = false;
_state.restHeadPosition.rx = detectState.rx;
_state.restHeadPosition.ry = detectState.ry;
_state.restHeadPosition.s = detectState.s;
_lastTimestamp = Date.now();
}
// compute movement of the camera
const ts = Date.now();
const dt = ts - _lastTimestamp;
_returnValue.dRx = dt * compute_delta(_state.restHeadPosition.rx, detectState.rx, _settings.tol.rx, _settings.sensibility.rx);
_returnValue.dRy = dt * compute_delta(_state.restHeadPosition.ry, detectState.ry, _settings.tol.ry, _settings.sensibility.ry);
_returnValue.dZ = dt * compute_delta(_state.restHeadPosition.s, detectState.s, _settings.tol.s, _settings.sensibility.s);
_lastTimestamp = ts;
return _returnValue;
} //end compute_cameraMove()
// public methods:
const that = {
init: function(spec){
// set settings:
if (typeof(spec.settings)==='undefined') spec.settings={};
_disableRestPosition = (typeof(spec.disableRestPosition)==='undefined') ? false : spec.disableRestPosition;
_settings = Object.assign({}, _defaultSettings, spec.settings);
_settings.tol.rx *= Math.PI / 180; // convert from degrees to radians
_settings.tol.ry *= Math.PI / 180;
_settings.tol.s /= 100;
// init the API:
JEELIZFACEFILTER.init({
canvasId: spec.canvasId,
NNCPath: spec.NNCPath, // root of NNC.json file
callbackReady: function(errCode, jeeFaceFilterObj){
if (errCode){
console.log('AN ERROR HAPPENS. SORRY BRO :( . ERR =', errCode);
if (spec.callbackReady){
spec.callbackReady(errCode);
}
return;
}
_gl = jeeFaceFilterObj['GL'];
_videoTexture = jeeFaceFilterObj['videoTexture'];
_videoTransformMat2 = jeeFaceFilterObj['videoTransformMat2'];
_cv = jeeFaceFilterObj['canvasElement'];
init_headSearchDraw();
if (spec.callbackReady){
spec.callbackReady(false);
}
_state.isLoaded = true;
}, //end callbackReady()
// called at each render iteration (drawing loop):
callbackTrack: function(detectState){
const mv = compute_cameraMove(detectState);
mv.expressions = detectState.expressions;
if (!_state.isEnabled){
return;
}
if (mv.dRx!==0 || mv.dRy!==0 || mv.dZ!==0){
spec.callbackMove(mv);
}
}
}); //end JEELIZFACEFILTER.init call
}, //end init()
toggle: function(isEnabled){
if (_state.isEnabled===isEnabled){
return true;
} else if (!isEnabled){ //disable
_state.isEnabled = false;
return true;
} else {
_state.isEnabled = true;
_state.restHeadPosition.needsUpdate = true;
return true;
}
},
reset_restHeadPosition: function(){
_state.restHeadPosition.needsUpdate = true;
}
}; //end that
return that;
})();
// Export ES6 module:
try {
module.exports = HeadControls;
} catch(e){
console.log('HeadControls ES6 Module not exported');
}

View File

@ -0,0 +1,141 @@
/*
Usage: JeelizCanvas2DHelper(spec) where spec is the returned object of the initialization function (callbackReady)
Return an object width these properties:
- canvas: the CANVAS element
- ctx: the canvas drawing context
- update_canvasTexture: function to launch each time the canvas has been updated (somethink has been drawn on it)
- draw: draw the video with the canvas above
- getCoordinates: transform the detectedState relative 2D viewport coords into canvas 2D pixel coordinates
- resize: to call if the HTML canvas size has changed
*/
const JeelizCanvas2DHelper = function(spec){
// some globalz:
let CV = null, CANVAS2D = null, CTX = null, GL = null, CANVASTEXTURE = null, CANVASTEXTURENEEDSUPDATE = null, SHADERCOPY = null, VIDEOTEXTURE = null;
let VIDEOTEXTURETRANSFORMMAT2 = null, UUVTRANSFORM = null;
const COORDINATES = {
x:0, y:0, s:0
};
//BEGIN WEBGL HELPERS
// compile a shader:
function compile_shader(source, glType, typeString) {
const glShader = GL.createShader(glType);
GL.shaderSource(glShader, source);
GL.compileShader(glShader);
if (!GL.getShaderParameter(glShader, GL.COMPILE_STATUS)) {
alert("ERROR IN " + typeString + " SHADER: " + GL.getShaderInfoLog(glShader));
return null;
}
return glShader;
};
// helper function to build the shader program:
function build_shaderProgram(shaderVertexSource, shaderFragmentSource, id) {
// compile both shader separately:
const glShaderVertex = compile_shader(shaderVertexSource, GL.VERTEX_SHADER, "VERTEX " + id);
const glShaderFragment = compile_shader(shaderFragmentSource, GL.FRAGMENT_SHADER, "FRAGMENT " + id);
const glShaderProgram = GL.createProgram();
GL.attachShader(glShaderProgram, glShaderVertex);
GL.attachShader(glShaderProgram, glShaderFragment);
// start the linking stage:
GL.linkProgram(glShaderProgram);
return glShaderProgram;
}
//END WEBGL HELPERS
// affect some globalz:
GL = spec.GL;
CV = spec.canvasElement;
VIDEOTEXTURE = spec.videoTexture;
VIDEOTEXTURETRANSFORMMAT2 = spec.videoTransformMat2;
// create and size the 2D canvas and its drawing context:
CANVAS2D = document.createElement('canvas');
CANVAS2D.width = CV.width;
CANVAS2D.height = CV.height;
CTX = CANVAS2D.getContext('2d');
// create the WebGL texture with the canvas:
CANVASTEXTURE = GL.createTexture();
GL.bindTexture(GL.TEXTURE_2D, CANVASTEXTURE);
GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, CANVAS2D);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
// build the copy shader program:
const copyVertexShaderSource = "attribute vec2 position;\n\
uniform mat2 UVTransformMat2;\n\
varying vec2 vUV;\n\
void main(void){\n\
gl_Position = vec4(position, 0., 1.);\n\
vUV = vec2(0.5,0.5) + UVTransformMat2 * position;\n\
}";
const copyFragmentShaderSource = "precision lowp float;\n\
uniform sampler2D samplerImage;\n\
varying vec2 vUV;\n\
\n\
void main(void){\n\
gl_FragColor = texture2D(samplerImage, vUV);\n\
}";
SHADERCOPY = build_shaderProgram(copyVertexShaderSource, copyFragmentShaderSource, 'VIDEO');
const uSampler = GL.getUniformLocation(SHADERCOPY, 'samplerImage');
UUVTRANSFORM = GL.getUniformLocation(SHADERCOPY, 'UVTransformMat2');
GL.useProgram(SHADERCOPY);
GL.uniform1i(uSampler, 0);
return {
canvas: CANVAS2D,
ctx: CTX,
update_canvasTexture: function(){
CANVASTEXTURENEEDSUPDATE = true;
},
draw: function(){ // draw the video and the canvas above
GL.viewport(0, 0, CV.width, CV.height);
GL.useProgram(SHADERCOPY);
// enable blending:
GL.enable(GL.BLEND);
GL.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA);
// draw the video first:
GL.bindTexture(GL.TEXTURE_2D, VIDEOTEXTURE);
GL.uniformMatrix2fv(UUVTRANSFORM, false, VIDEOTEXTURETRANSFORMMAT2);
GL.drawElements(GL.TRIANGLES, 3, GL.UNSIGNED_SHORT, 0);
// then draw the canvas:
GL.bindTexture(GL.TEXTURE_2D, CANVASTEXTURE);
GL.uniformMatrix2fv(UUVTRANSFORM, false, [0.5, 0, 0, 0.5]);
if (CANVASTEXTURENEEDSUPDATE) {
GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, CANVAS2D);
}
GL.drawElements(GL.TRIANGLES, 3, GL.UNSIGNED_SHORT, 0);
GL.disable(GL.BLEND);
},
getCoordinates: function(detectedState){
COORDINATES.x = Math.round((0.5+0.5*detectedState.x-0.5*detectedState.s)*CV.width);
COORDINATES.y = Math.round((0.5+0.5*detectedState.y-0.5*detectedState.s)*CV.height);
COORDINATES.w = Math.round(detectedState.s*CV.width);
COORDINATES.h = COORDINATES.w;
return COORDINATES;
},
resize: function(){
CANVAS2D.width = CV.width;
CANVAS2D.height = CV.height;
}
}; //end Canvas2DDisplay return value
} //end JeelizCanvas2DHelper()

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