Skip to content

Commit

Permalink
Merge branch 'obsproject:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
decahedron1 authored Dec 28, 2023
2 parents 3aad5a6 + 0e50077 commit 4d51b76
Show file tree
Hide file tree
Showing 128 changed files with 2,179 additions and 1,593 deletions.
12 changes: 10 additions & 2 deletions .github/actions/services-validator/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ inputs:
repositorySecret:
description: GitHub token for API access
required: true
checkApiSecret:
description: Token for server check API
required: false
checkApiServers:
description: Servers for the check API
required: false
runSchemaChecks:
description: Enable schema checking
required: false
Expand Down Expand Up @@ -46,8 +52,8 @@ runs:
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
fi
brew install --overwrite --quiet python3
python3 -m pip install jsonschema json_source_map requests
brew install --overwrite --quiet python@3.11
python3 -m pip install jsonschema json_source_map requests aiohttp
echo ::endgroup::
- name: Validate Services File JSON Schema 🕵️
Expand Down Expand Up @@ -91,6 +97,8 @@ runs:
GITHUB_TOKEN: ${{ inputs.repositorySecret }}
WORKFLOW_RUN_ID: ${{ github.run_id }}
REPOSITORY: ${{ github.repository }}
API_KEY: ${{ inputs.checkApiSecret }}
API_SERVERS: ${{ inputs.checkApiServers }}
run: |
: Check for defunct services 📉
python3 -u .github/scripts/utils.py/check-services.py
Expand Down
161 changes: 76 additions & 85 deletions .github/scripts/utils.py/check-services.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import asyncio
import json
import socket
import ssl
import os
import time
import requests
import sys
import zipfile

import aiohttp
import requests

from io import BytesIO
from random import randbytes
from urllib.parse import urlparse
from typing import List, Dict
from collections import defaultdict

MINIMUM_PURGE_AGE = 9.75 * 24 * 60 * 60 # slightly less than 10 days
Expand Down Expand Up @@ -56,74 +56,6 @@
}
}"""

context = ssl.create_default_context()


def check_ftl_server(hostname) -> bool:
"""Check if hostname resolves to a valid address - FTL handshake not implemented"""
try:
socket.getaddrinfo(hostname, 8084, proto=socket.IPPROTO_UDP)
except socket.gaierror as e:
print(f"⚠️ Could not resolve hostname for server: {hostname} (Exception: {e})")
return False
else:
return True


def check_hls_server(uri) -> bool:
"""Check if URL responds with status code < 500 and not 404, indicating that at least there's *something* there"""
try:
r = requests.post(uri, timeout=TIMEOUT)
if r.status_code >= 500 or r.status_code == 404:
raise Exception(f"Server responded with {r.status_code}")
except Exception as e:
print(f"⚠️ Could not connect to HLS server: {uri} (Exception: {e})")
return False
else:
return True


def check_rtmp_server(uri) -> bool:
"""Try connecting and sending a RTMP handshake (with SSL if necessary)"""
parsed = urlparse(uri)
hostname, port = parsed.netloc.partition(":")[::2]

if port:
port = int(port)
elif parsed.scheme == "rtmps":
port = 443
else:
port = 1935

try:
recv = b""
with socket.create_connection((hostname, port), timeout=TIMEOUT) as sock:
# RTMP handshake is \x03 + 4 bytes time (can be 0) + 4 zero bytes + 1528 bytes random
handshake = b"\x03\x00\x00\x00\x00\x00\x00\x00\x00" + randbytes(1528)
if parsed.scheme == "rtmps":
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
ssock.sendall(handshake)
while True:
_tmp = ssock.recv(4096)
recv += _tmp
if len(recv) >= 1536 or not _tmp:
break
else:
sock.sendall(handshake)
while True:
_tmp = sock.recv(4096)
recv += _tmp
if len(recv) >= 1536 or not _tmp:
break

if len(recv) < 1536 or recv[0] != 3:
raise ValueError("Invalid RTMP handshake received from server")
except Exception as e:
print(f"⚠️ Connection to server failed: {uri} (Exception: {e})")
return False
else:
return True


def get_last_artifact():
s = requests.session()
Expand Down Expand Up @@ -219,7 +151,18 @@ def set_output(name, value):
print(f"Writing to github output files failed: {e!r}")


def main():
async def check_servers_task(
session: aiohttp.ClientSession, host: str, protocol: str, servers: List[str]
) -> List[Dict]:
query = [dict(url=h, protocol=protocol) for h in servers]

async with session.get(
f"http://{host}:8999/test_remote_servers", json=query
) as resp:
return await resp.json()


async def process_services(session: aiohttp.ClientSession, check_servers: List[str]):
try:
with open(SERVICES_FILE, encoding="utf-8") as services_file:
raw_services = services_file.read()
Expand Down Expand Up @@ -265,25 +208,47 @@ def main():
continue

service_type = service.get("recommended", {}).get("output", "rtmp_output")
if service_type not in {"rtmp_output", "ffmpeg_hls_muxer", "ftl_output"}:
if service_type not in {"rtmp_output", "ffmpeg_hls_muxer"}:
print("Unknown service type:", service_type)
new_services["services"].append(service)
continue

protocol = "rtmp" if service_type == "rtmp_output" else "hls"

# create a copy to mess with
new_service = service.copy()
new_service["servers"] = []

# run checks for all the servers, and store results in timestamp cache
for server in service["servers"]:
if service_type == "ftl_output":
is_ok = check_ftl_server(server["url"])
elif service_type == "ffmpeg_hls_muxer":
is_ok = check_hls_server(server["url"])
else: # rtmp
is_ok = check_rtmp_server(server["url"])
try:
servers = [s["url"] for s in service["servers"]]
tasks = []
for host in check_servers:
tasks.append(
asyncio.create_task(
check_servers_task(session, host, protocol, servers)
)
)
results = await asyncio.gather(*tasks)
except Exception as e:
print(
f"❌ Querying server status for \"{service['name']}\" failed with: {e}"
)
return 1

# go over results
for server, result in zip(service["servers"], zip(*results)):
failure_count = sum(not res["status"] for res in result)
probe_count = len(result)
# only treat server as failed if all check servers reported a failure
is_ok = failure_count < probe_count

if not is_ok:
failures = {res["comment"] for res in result if not res["status"]}
print(
f"⚠️ Connecting to server failed: {server['url']} (Reason(s): {failures})"
)

if ts := fail_timestamps.get(server["url"], None):
if (delta := start_time - ts) >= MINIMUM_PURGE_AGE:
print(
Expand Down Expand Up @@ -313,9 +278,11 @@ def main():
if not new_service["servers"]:
print(f'💀 Service "{service["name"]}" has no valid servers left, removing!')
affected_services[service["name"]] = f"Service removed"
continue
else:
new_services["services"].append(new_service)

new_services["services"].append(new_service)
# wait a bit between services
await asyncio.sleep(2.0)

# write cache file
try:
Expand Down Expand Up @@ -377,5 +344,29 @@ def main():
set_output("make_pr", "false")


async def main():
# check for environment variables
try:
api_key = os.environ["API_KEY"]
servers = os.environ["API_SERVERS"].split(",")
if not servers:
raise ValueError("No checker servers!")
# Mask everything except the region code
for server in servers:
prefix = server[: server.index(".") + 1]
print(f"::add-mask::{prefix}")
suffix = server[server.index(".", len(prefix)) :]
print(f"::add-mask::{suffix}")
except Exception as e:
print(f"❌ Failed getting required environment variables: {e}")
return 1

# create aiohttp session
async with aiohttp.ClientSession() as session:
session.headers["Authorization"] = api_key
session.headers["User-Agent"] = "OBS Repo Service Checker/1.0"
return await process_services(session, servers)


if __name__ == "__main__":
sys.exit(main())
sys.exit(asyncio.run(main()))
2 changes: 2 additions & 0 deletions .github/workflows/dispatch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ jobs:
uses: ./.github/actions/services-validator
with:
repositorySecret: ${{ secrets.GITHUB_TOKEN }}
checkApiSecret: ${{ secrets.CHECK_SERVERS_API_KEY }}
checkApiServers: ${{ secrets.CHECK_SERVERS_LIST }}
runSchemaChecks: true
runServiceChecks: true
createPullRequest: true
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/scheduled.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ jobs:
uses: ./.github/actions/services-validator
with:
repositorySecret: ${{ secrets.GITHUB_TOKEN }}
checkApiSecret: ${{ secrets.CHECK_SERVERS_API_KEY }}
checkApiServers: ${{ secrets.CHECK_SERVERS_LIST }}
runSchemaChecks: false
runServiceChecks: true
createPullRequest: true
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
!/docs
!/libobs*
!/plugins
!/tests
!/test
!/UI
!.cirrus.xml
!.clang-format
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ Coding Guidelines

- Comments and names of variables/functions/etc. must be in English

- Formatting scripts (mac0S/Linux only) are available `here <./build-aux>`__

Commit Guidelines
-----------------

Expand Down
4 changes: 1 addition & 3 deletions UI/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ target_sources(
platform.hpp
qt-display.cpp
qt-display.hpp
qt-wrappers.cpp
qt-wrappers.hpp
ui-config.h
ui-validation.cpp
ui-validation.hpp)
Expand All @@ -91,7 +89,7 @@ elseif(OS_MACOS)
include(cmake/os-macos.cmake)
elseif(OS_LINUX)
include(cmake/os-linux.cmake)
elseif(OS_FREEBSD)
elseif(OS_FREEBSD OR OS_OPENBSD)
include(cmake/os-freebsd.cmake)
endif()

Expand Down
6 changes: 4 additions & 2 deletions UI/cmake/feature-browserpanels.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ if(TARGET OBS::browser-panels)

target_link_libraries(obs-studio PRIVATE OBS::browser-panels)

target_sources(obs-studio PRIVATE window-dock-browser.cpp window-dock-browser.hpp window-extra-browsers.cpp
window-extra-browsers.hpp)
target_sources(
obs-studio
PRIVATE # cmake-format: sortable
window-dock-browser.cpp window-dock-browser.hpp window-extra-browsers.cpp window-extra-browsers.hpp)
endif()
7 changes: 5 additions & 2 deletions UI/cmake/feature-importers.cmake
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
target_sources(obs-studio PRIVATE importers/classic.cpp importers/importers.cpp importers/importers.hpp
importers/sl.cpp importers/studio.cpp importers/xsplit.cpp)
target_sources(
obs-studio
PRIVATE # cmake-format: sortable
importers/classic.cpp importers/importers.cpp importers/importers.hpp importers/sl.cpp importers/studio.cpp
importers/xsplit.cpp)
2 changes: 2 additions & 0 deletions UI/cmake/feature-whatsnew.cmake
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
include_guard(DIRECTORY)

option(ENABLE_WHATSNEW "Enable WhatsNew dialog" ON)

if(ENABLE_WHATSNEW AND TARGET OBS::browser-panels)
Expand Down
3 changes: 2 additions & 1 deletion UI/cmake/feature-youtube.cmake
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
if(YOUTUBE_CLIENTID
AND YOUTUBE_SECRET
AND YOUTUBE_CLIENTID_HASH MATCHES "^(0|[a-fA-F0-9]+)$"
AND YOUTUBE_SECRET_HASH MATCHES "^(0|[a-fA-F0-9]+)$")
AND YOUTUBE_SECRET_HASH MATCHES "^(0|[a-fA-F0-9]+)$"
AND TARGET OBS::browser-panels)
target_sources(
obs-studio
PRIVATE # cmake-format: sortable
Expand Down
4 changes: 0 additions & 4 deletions UI/cmake/legacy.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,6 @@ target_sources(
hotkey-edit.hpp
lineedit-autoresize.cpp
lineedit-autoresize.hpp
locked-checkbox.cpp
locked-checkbox.hpp
log-viewer.cpp
log-viewer.hpp
media-controls.cpp
Expand Down Expand Up @@ -219,8 +217,6 @@ target_sources(
volume-control.hpp
vertical-scroll-area.cpp
vertical-scroll-area.hpp
visibility-checkbox.cpp
visibility-checkbox.hpp
visibility-item-widget.cpp
visibility-item-widget.hpp)

Expand Down
8 changes: 1 addition & 7 deletions UI/cmake/os-macos.cmake
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
if(NOT XCODE)
target_add_resource(obs-studio "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Assets.xcassets")
endif()

target_sources(obs-studio PRIVATE platform-osx.mm forms/OBSPermissions.ui window-permissions.cpp window-permissions.hpp)
target_compile_options(
obs-studio PRIVATE -Wno-error=float-conversion -Wno-error=implicit-int-conversion -Wno-error=shorten-64-to-32
-Wno-quoted-include-in-framework-header -Wno-comma)
target_compile_options(obs-studio PRIVATE -Wno-quoted-include-in-framework-header -Wno-comma)

set_source_files_properties(platform-osx.mm PROPERTIES COMPILE_FLAGS -fobjc-arc)

Expand Down
Loading

0 comments on commit 4d51b76

Please sign in to comment.