5 Commits

835 changed files with 30986 additions and 33182 deletions

View File

@@ -1 +0,0 @@
test/automated/api/node_modules

View File

@@ -1,17 +1,12 @@
# Read first
If this is an unsolicited change, or there is no existing issue filed for it, please open a GitHub issue before creating a pull request. This will allow us to discuss the motivations and the big picture behind the change first. It's possible there may be other solutions that should be discussed for what you think should be built. It is possible your change will be rejected unless some discussion around your proposal happens first. While creating this PR means you probably already did the work, it still makes sense to file an issue now, and into the future when you have proposed changes.
Please include a summary of the change and which issue number is fixed, including relevant motivation and context. Feel free to mark this as a Draft or WIP and write up some details later.
## Description
If there is no issue filed for this particular change it's highly recommended you file one. While creating this PR means you probably already did the work, in the future make sure an issue is filed beforehand so changes, fixes and features can be discussed ahead of time.
Please include a summary of the change and which issue number is fixed, including relevant motivation and context. Feel free to mark this as a Draft or WIP and write up some details later and start a conversation, even if your PR is not ready for review.
# Description
Fixes # (issue)
## Screenshot Examples or Logs
If this is a frontend change, please include a screenshot of the change. If this is a backend change, please include relevant logs or examples of the change in action if applicable.
---
Some things you might want to mention:
@@ -21,4 +16,4 @@ Some things you might want to mention:
3. If you're fixing something, what was wrong? How should we stop from having this issue happen again?
4. If this is a new feature or addition to functionality, why should it be added? What are the use cases? Who was asking for this functionality?
Thank you so much for contributing to Owncast! 🎉
If this is an unsolicited change or have no issue associated please do your best to detail the motivations behind this PR, and think about filing an issue to discuss changes ahead of time in the future.

31
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- backlog
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. If this
was a feature request that others have shown no interest in then it's
likely to not get implemented due to lack of interest. If others also
want to see this feature then now is the time to say something!
Thank you for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
exemptMilestones: true
# Since old PRs are less useful than old issues ping them sooner.
pulls:
daysUntilStale: 30
markComment: >
This pull request has not had any activity in 30 days. Since things move fast it's best
to get PRs merged in. If this PR addresses a previously filed issue that needs to be
resolved please work to get it merged in, or allow somebody else to work on a fix.
This PR will be closed if no further activity occurs. Thank you for your contributions!
exemptLabels:
- bot

View File

@@ -13,15 +13,7 @@ jobs:
name: GitHub actions
runs-on: ubuntu-latest
steps:
- name: Check out pull request code
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- uses: actions/checkout@v4
- uses: docker://rhysd/actionlint:latest
with:

View File

@@ -11,7 +11,7 @@ jobs:
issues: write
steps:
- name: Add comment
uses: peter-evans/create-or-update-comment@fdb73c443d3a4f66832374f01fb9a713fad84937
uses: peter-evans/create-or-update-comment@71ac479718f8aed504782bc920d802da994c05fa
with:
issue-number: ${{ github.event.issue.number }}
body: |

View File

@@ -18,43 +18,23 @@ jobs:
with:
concurrent_skipping: 'same_content_newer'
- name: Check out pull request code
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- name: Get changed files
id: changed-files-yaml
uses: tj-actions/changed-files@v45
with:
files_yaml: |
src:
- '**/*.{go,mod,sum}'
- uses: earthly/actions-setup@v1
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
with:
version: 'latest' # or pin to an specific version, e.g. "v0.6.10"
- name: Earthly version
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
run: earthly --version
- name: Set up QEMU
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
id: qemu
uses: docker/setup-qemu-action@v3
with:
image: tonistiigi/binfmt:latest
platforms: all
- uses: actions/checkout@v4
- name: Run API tests
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
uses: nick-fields/retry@v3
with:
timeout_minutes: 10

View File

@@ -19,19 +19,12 @@ jobs:
with:
concurrent_skipping: 'same_content_newer'
- name: Check out pull request code
- name: Checkout
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- uses: actions/setup-node@v4
with:
node-version: '22.9.0'
node-version: latest
- name: Cache node modules
uses: actions/cache@v4

View File

@@ -11,15 +11,8 @@ jobs:
if: github.repository == 'owncast/owncast'
steps:
- name: Check out pull request code
- name: Checkout
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- name: Cache node modules
uses: actions/cache@v4

View File

@@ -27,37 +27,23 @@ jobs:
uses: fkirc/skip-duplicate-actions@v5
with:
concurrent_skipping: 'same_content_newer'
- name: Check out pull request code
- name: Check out code
if: ${{ github.actor != 'renovate[bot]' && github.actor != 'renovate' }}
uses: actions/checkout@v4
if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target'
with:
# Make sure the actual branch is checked out when running on pull requests
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- name: Get changed files
id: changed-files-yaml
uses: tj-actions/changed-files@v45
with:
path: 'web'
files_ignore: |
static/**
web/next.config.js
files_yaml: |
src:
- '**/*.{js,ts,tsx,jsx,md}'
fetch-depth: 0
- name: Install dependencies
if: ${{ github.actor != 'renovate[bot]' && github.actor != 'renovate' && steps.changed-files-yaml.outputs.src_any_changed == 'true'}}
if: ${{ github.actor != 'renovate[bot]' && github.actor != 'renovate' }}
run: npm install
- name: Publish to Chromatic
if: ${{ github.actor != 'renovate[bot]' && github.actor != 'renovate' && steps.changed-files-yaml.outputs.src_any_changed == 'true' }}
uses: chromaui/action@v11
if: ${{ github.actor != 'renovate[bot]' && github.actor != 'renovate' }}
uses: chromaui/action@v11
# Chromatic GitHub Action options
with:
workingDir: web

View File

@@ -36,15 +36,8 @@ jobs:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Check out pull request code
- name: Checkout repository
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- uses: actions/setup-go@v5
with:

View File

@@ -19,15 +19,7 @@ jobs:
container:
image: aquasec/trivy
steps:
- name: Check out pull request code
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- uses: actions/checkout@v4
- name: Check critical issues
run: trivy config --exit-code 1 --severity "HIGH,CRITICAL" ./Dockerfile

View File

@@ -37,15 +37,10 @@ jobs:
image: tonistiigi/binfmt:latest
platforms: all
- name: Check out pull request code
- name: Checkout repo
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push' || github.event_name == 'schedule'
fetch-depth: 0
- name: Build and push
if: ${{ github.event_name == 'schedule' && env.GH_CR_PAT != null }}

View File

@@ -1,53 +0,0 @@
name: CSS Lint and Formatting
on:
push:
paths:
- 'web/**'
pull_request:
paths:
- 'web/**'
jobs:
css-lint:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./web
steps:
- name: Check out pull request code
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- name: Get changed files
id: changed-files-yaml
uses: tj-actions/changed-files@v45
with:
path: 'web'
files_yaml: |
src:
- '**/*.{css,scss}'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22.9.0'
- name: Install dependencies
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
run: npm install
- name: Run Prettier
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
run: npx prettier --check ${{ steps.changed-files-yaml.outputs.src_all_changed_files }}
- name: Run Stylelint
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
run: npx stylelint ${{ steps.changed-files-yaml.outputs.src_all_changed_files }}

View File

@@ -9,15 +9,8 @@ jobs:
name: Generate API Documentation
runs-on: ubuntu-latest
steps:
- name: Check out pull request code
- name: Checkout repo
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- name: Run redoc on openapi.yaml
run: |

View File

@@ -18,16 +18,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Get changed files
id: changed-files-yaml
uses: tj-actions/changed-files@v45
with:
files_yaml: |
src:
- '**/*.{go,mod,sum}'
- uses: actions/cache@v4
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
with:
path: |
~/.cache/go-build
@@ -37,14 +28,12 @@ jobs:
go-test-
- name: Install go
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
uses: actions/setup-go@v5
with:
go-version: '^1'
cache: true
- name: Run tests
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
run: go test ./...
test-bsds:
@@ -60,14 +49,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Get changed files
id: changed-files-yaml
uses: tj-actions/changed-files@v45
with:
files_yaml: |
src:
- '**/*.{go,mod,sum}'
- uses: actions/cache@v4
with:
path: |
@@ -78,12 +59,10 @@ jobs:
go-test-
- name: Install go
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
uses: actions/setup-go@v5
with:
go-version: '^1'
cache: true
- name: Run tests
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
run: go test ./...

View File

@@ -24,27 +24,8 @@ jobs:
uses: fkirc/skip-duplicate-actions@v5
with:
concurrent_skipping: 'same_content_newer'
- name: Check out pull request code
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- name: Get changed files
id: changed-files-yaml
uses: tj-actions/changed-files@v45
with:
files_yaml: |
src:
- '**/*.{go,mod,sum}'
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
with:
go-version: '1.22'
cache: true
@@ -62,7 +43,6 @@ jobs:
${{ runner.os }}-
- name: Local stroage
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
@@ -70,7 +50,6 @@ jobs:
command: cd test/automated/hls && ./run.sh
- name: S3 storage
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
uses: nick-fields/retry@v3
with:
timeout_minutes: 10

View File

@@ -28,25 +28,18 @@ jobs:
cancel_others: 'true'
skip_after_successful_duplicate: 'true'
- name: Check out pull request code
- name: Checkout
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
# Make sure the actual branch is checked out when running on pull requests
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{github.event.pull_request.head.ref}}
- name: Setup Nodejs
uses: actions/setup-node@v4
with:
node-version: '22.9.0'
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
fetch-depth: 0
persist-credentials: true
- name: Get changed files
id: changed-files-yaml
uses: tj-actions/changed-files@v45
uses: tj-actions/changed-files@v44
with:
path: 'web'
files_ignore: |
@@ -54,7 +47,7 @@ jobs:
web/next.config.js
files_yaml: |
src:
- '**/*.{js,ts,tsx,jsx,md}'
- '**/*.{js,ts,tsx,jsx,css,md}'
- name: Cache node modules
uses: actions/cache@v4
@@ -71,27 +64,19 @@ jobs:
- name: Install Dependencies
run: npm install
- name: Lint and fix
if: steps.changed-files-yaml.outputs.src_any_changed == 'true' && github.event_name != 'pull_request'
- name: Lint
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
run: npx eslint --fix ${{ steps.changed-files-yaml.outputs.src_all_changed_files }}
- name: Lint
if: steps.changed-files-yaml.outputs.src_any_changed == 'true' && github.event_name == 'pull_request'
run: npx eslint ${{ steps.changed-files-yaml.outputs.src_all_changed_files }}
- name: Prettier formatting
if: steps.changed-files-yaml.outputs.src_any_changed == 'true' && github.event_name == 'pull_request'
- name: Prettier
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
run: npx prettier --write ${{ steps.changed-files-yaml.outputs.src_all_changed_files }}
- name: Prettier check
if: steps.changed-files-yaml.outputs.src_any_changed == 'true' && github.event_name != 'pull_request'
run: npx prettier ${{ steps.changed-files-yaml.outputs.src_all_changed_files }}
- name: Debug changed files output
run: 'pwd && echo "Changed files: ${{ steps.changed-files-yaml.outputs.src_all_changed_files }}"'
- name: Commit changes
if: steps.changed-files-yaml.outputs.src_any_changed == 'true' && github.event_name != 'pull_request'
if: steps.changed-files-yaml.outputs.src_any_changed == 'true'
uses: EndBug/add-and-commit@v9
with:
author_name: Owncast
@@ -116,20 +101,13 @@ jobs:
cancel_others: 'true'
skip_after_successful_duplicate: 'true'
- name: Check out pull request code
- name: Checkout
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
# Make sure the actual branch is checked out when running on pull requests
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- name: Setup Nodejs
uses: actions/setup-node@v4
with:
node-version: '22.9.0'
fetch-depth: 0
- name: Cache node modules
uses: actions/cache@v4
@@ -149,10 +127,6 @@ jobs:
- name: Check for unused JS code and dependencies
run: npx knip --include dependencies,files,exports
- name: Run tests
working-directory: ./web
run: npm test
# After any formatting and linting is complete we can run the build
# and bundle step. This both will verify that the build is successful as
# well as commiting the updated static files into the repository for use.
@@ -169,11 +143,6 @@ jobs:
cancel_others: 'true'
skip_after_successful_duplicate: 'true'
- name: Setup Nodejs
uses: actions/setup-node@v4
with:
node-version: '22.9.0'
- name: Cache node modules
uses: actions/cache@v4
env:
@@ -186,15 +155,13 @@ jobs:
${{ runner.os }}-build-
${{ runner.os }}-
- name: Check out pull request code
- name: Checkout
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
# Make sure the actual branch is checked out when running on pull requests
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
fetch-depth: 0
- name: Bundle web app (next.js build)
run: build/web/bundleWeb.sh

45
.github/workflows/javascript-tests.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: Javascript Tests
on:
push:
paths:
- 'web/**'
pull_request:
paths:
- 'web/**'
jobs:
jest-run:
runs-on: ubuntu-latest
steps:
- id: skip_check
uses: fkirc/skip-duplicate-actions@v5
with:
concurrent_skipping: 'same_content_newer'
- name: Checkout
uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18.9.0
- name: Cache node modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules-javascript-tests
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('web/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Install Dependencies
working-directory: ./web
run: npm install
- name: Run tests
working-directory: ./web
run: npm test

58
.github/workflows/screenshots.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: Take nightly screenshots
on:
schedule:
- cron: '0 4 * * *'
env:
BROWSERSTACK_KEY: ${{ secrets.BROWSERSTACK_KEY }}
BROWSERSTACK_PASSWORD: ${{ secrets.BROWSERSTACK_PASSWORD }}
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
TEST_URL: http://localhost:8080
jobs:
Screenshots:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
- name: Cache node modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules-screenshots
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('test/automated/screenshots/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Automate screenshots
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 4
command: cd test/automated/screenshots && ./run.sh
- name: Commit changes
uses: EndBug/add-and-commit@v9
with:
author_name: Owncast
author_email: owncast@owncast.online
message: 'Commit screenshots'
add: '*.png'
pull: '--rebase --autostash'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Dispatch event to web site
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.BUNDLE_STORYBOOK_OWNCAST_ONLINE }}
repository: owncast/owncast.github.io
event-type: bundle-components-library

View File

@@ -20,15 +20,7 @@ jobs:
container:
image: docker.io/ubuntu:24.04
steps:
- name: Check out pull request code
uses: actions/checkout@v4
if: github.event_name == 'pull_request'
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Check out repository code
uses: actions/checkout@v4
if: github.event_name == 'push'
- uses: actions/checkout@v4
- name: Install shellcheck
run: apt update && apt install -y shellcheck bash && shellcheck --version

View File

@@ -1,49 +0,0 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 */2 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
exempt-all-milestones: true
days-before-issue-stale: 60
days-before-issue-close: 67
exempt-issue-labels: backlog,long-lived,bot
exempt-all-issue-milestones: true
stale-issue-message: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. If this
was a feature request that others have shown no interest in, then it's
unlikely to get implemented due to lack of interest. If others also
want to see this feature then now is the time to say something! If this
is a bug report or you have questions that still need answering, please say
something. Feel free to drop by [our chat](https://owncast.rocket.chat) if
you'd like to discuss in real-time with people.
close-issue-message: >
This issue has been automatically closed due to inactivity. This isn't done
to be a jerk, or because the project doesn't care. But simply to keep the focus
on things that are actively discussed, and has continued interest from the community and
Owncast developers. Feel free to to comment if there is still discussion to be
had, or if you plan to work on it. Feel free to drop by [our chat](https://owncast.rocket.chat)
if you'd like to discuss in real-time with people. Thank you for being involved!
days-before-pr-stale: 30
days-before-pr-close: 37
exempt-pr-labels: backlog,long-lived,bot
exempt-all-pr-milestones: true
stale-pr-message: >
This pull request has not had any activity in 30 days. If it has been abandoned
no future actions are necessary, it will be automatically closed. If this is a PR
with no clear plan on how to move forward on it getting into the project, then
further discussion is needed. Now is a good time to discuss if this is still
something that should be worked on. If this PR is idle simply because nobody
has reviewed it, then feel free to ping somebody. However, if this PR is not linked to an
existing issue regarding something that was previously determined to be important, then even
more discussion needs to take place before it can get anywhere.
This PR will be closed if no further activity occurs. Thank you for your contributions!
close-pr-message: 'This PR was closed because it has been stalled for 10 days with no activity.'

View File

@@ -1,62 +0,0 @@
name: Translation job
on:
schedule:
# Run the workflow every hour
- cron: "0 * * * *"
push:
paths:
- 'web/i18n/en/translation.json'
- 'web/**/*.tsx'
- 'web/**/*.js'
- 'crowdin.yml'
- '.github/workflows/translations.yml'
- 'web/i18next-parser.config.mjs'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
generate-translations:
defaults:
run:
working-directory: ./web
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
if: ${{ github.actor != 'renovate[bot]' && github.actor != 'renovate' }}
run: npm install
- name: Generate translation files
run: npm run translate
- name: Crowdin upload sources/download translations
uses: crowdin/github-action@v1
with:
upload_sources: true
download_translations: true
create_pull_request: true
pull_request_title: 'New Translations'
localization_branch_name: translations
pull_request_base_branch_name: 'develop'
commit_message: 'Updated translations'
config: crowdin.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
- name: Commit changes
uses: EndBug/add-and-commit@v9
with:
author_name: Owncast
author_email: owncast@owncast.online
message: 'Commit updated translations'
add: 'web/i18n/**'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@@ -4,7 +4,6 @@
*.dll
*.so
*.dylib
.DS_Store
# Test binary, built with `go test -c`
*.test

View File

@@ -28,6 +28,7 @@ linters:
- bodyclose
- dupl
- errcheck
- exportloopref
- goconst
- godot
- godox
@@ -48,7 +49,7 @@ linters:
- cyclop
- gosimple
- unused
- copyloopvar
- exportloopref
- gocritic
- forbidigo
- unparam
@@ -66,6 +67,12 @@ linters-settings:
# should ignore tests
skip-tests: true
gosimple:
# Select the Go version to target. The default is '1.13'.
go: '1.22'
# https://staticcheck.io/docs/options#checks
checks: ['all']
gocritic:
disabled-checks:
- ifElseChain

View File

@@ -22,7 +22,7 @@ ENV NAME=${NAME}
RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -ldflags "-extldflags \"-static\" -s -w -X github.com/owncast/owncast/config.GitCommit=$GIT_COMMIT -X github.com/owncast/owncast/config.VersionNumber=$VERSION -X github.com/owncast/owncast/config.BuildPlatform=$NAME" -o owncast .
# Create the image by copying the result of the build into a new alpine image
FROM alpine:3.21.2
FROM alpine:3.20.1
RUN apk update && apk add --no-cache ffmpeg ffmpeg-libs ca-certificates && update-ca-certificates
RUN addgroup -g 101 -S owncast && adduser -u 101 -S owncast -G owncast

View File

@@ -1,6 +1,6 @@
VERSION --new-platform 0.6
FROM --platform=linux/amd64 alpine:3.21.2
FROM --platform=linux/amd64 alpine:3.15.5
ARG version=develop
WORKDIR /build
@@ -87,7 +87,7 @@ build:
RUN upx -t owncast
END
SAVE ARTIFACT --keep-ts owncast owncast
SAVE ARTIFACT owncast owncast
package:
RUN apk add --update --no-cache zip >> /dev/null
@@ -109,7 +109,7 @@ package:
ARG NAME=custom
END
COPY --keep-ts (+build/owncast --platform $TARGETPLATFORM) /build/dist/owncast
COPY (+build/owncast --platform $TARGETPLATFORM) /build/dist/owncast
ENV ZIPNAME owncast-$version-$NAME.zip
RUN cd /build/dist && zip -r -q -8 /build/dist/owncast.zip .
SAVE ARTIFACT --keep-ts /build/dist/owncast.zip owncast.zip AS LOCAL dist/$ZIPNAME
@@ -119,11 +119,11 @@ docker:
# in as space separated strings using the full account/repo:tag format.
# https://github.com/earthly/earthly/blob/aea38448fa9c0064b1b70d61be717ae740689fb9/docs/earthfile/earthfile.md#assigning-multiple-image-names
ARG TARGETPLATFORM
FROM --platform=$TARGETPLATFORM alpine:3.21.2
FROM --platform=$TARGETPLATFORM alpine:3.15.5
RUN apk update && apk add --no-cache ffmpeg ffmpeg-libs ca-certificates unzip && update-ca-certificates
RUN addgroup -g 101 -S owncast && adduser -u 101 -S owncast -G owncast
WORKDIR /app
COPY --keep-ts --platform=$TARGETPLATFORM +package/owncast.zip /app
COPY --platform=$TARGETPLATFORM +package/owncast.zip /app
RUN unzip -x owncast.zip && mkdir data
# temporarily disable until we figure out how to move forward

View File

@@ -1,14 +1,11 @@
package activitypub
import (
"math"
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/inbox"
"github.com/owncast/owncast/activitypub/outbox"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/workerpool"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
@@ -17,35 +14,21 @@ import (
// Start will initialize and start the federation support.
func Start(datastore *data.Datastore) {
configRepository := configrepository.Get()
persistence.Setup(datastore)
outboundWorkerPoolSize := getOutboundWorkerPoolSize()
workerpool.InitOutboundWorkerPool(outboundWorkerPoolSize)
workerpool.InitOutboundWorkerPool()
inbox.InitInboxWorkerPool()
// Generate the keys for signing federated activity if needed.
if configRepository.GetPrivateKey() == "" {
if data.GetPrivateKey() == "" {
privateKey, publicKey, err := crypto.GenerateKeys()
_ = configRepository.SetPrivateKey(string(privateKey))
_ = configRepository.SetPublicKey(string(publicKey))
_ = data.SetPrivateKey(string(privateKey))
_ = data.SetPublicKey(string(publicKey))
if err != nil {
log.Errorln("Unable to get private key", err)
}
}
}
func getOutboundWorkerPoolSize() int {
var followerCount int64
fc, err := persistence.GetFollowerCount()
if err != nil {
log.Errorln("Unable to get follower count", err)
fc = 50 // Arbitrary fallback value.
}
followerCount = int64(math.Max(float64(fc), 50))
return int(followerCount * 5)
}
// SendLive will send a "Go Live" message to followers.
func SendLive() error {
return outbox.SendLive()

View File

@@ -6,7 +6,7 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
)
// PrivacyAudience represents the audience for an activity.
@@ -87,10 +87,8 @@ func MakeActivityDirect(activity vocab.ActivityStreamsCreate, toIRI *url.URL) vo
// MakeActivityPublic sets the required properties to make this activity
// seen as public.
func MakeActivityPublic(activity vocab.ActivityStreamsCreate) vocab.ActivityStreamsCreate {
configRepository := configrepository.Get()
// TO the public if we're not treating ActivityPub as "private".
if !configRepository.GetFederationIsPrivate() {
if !data.GetFederationIsPrivate() {
public, _ := url.Parse(PUBLIC)
to := streams.NewActivityStreamsToProperty()
@@ -123,9 +121,7 @@ func MakeUpdateActivity(activityID *url.URL) vocab.ActivityStreamsUpdate {
activity.SetJSONLDId(id)
// CC the public if we're not treating ActivityPub as "private".
configRepository := configrepository.Get()
if !configRepository.GetFederationIsPrivate() {
if !data.GetFederationIsPrivate() {
public, _ := url.Parse(PUBLIC)
cc := streams.NewActivityStreamsCcProperty()
cc.AppendIRI(public)

View File

@@ -9,8 +9,8 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
)
@@ -101,13 +101,11 @@ func MakeActorPropertyWithID(idIRI *url.URL) vocab.ActivityStreamsActorProperty
// MakeServiceForAccount will create a new local actor service with the the provided username.
func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
configRepository := configrepository.Get()
actorIRI := MakeLocalIRIForAccount(accountName)
person := streams.NewActivityStreamsService()
nameProperty := streams.NewActivityStreamsNameProperty()
nameProperty.AppendXMLSchemaString(configRepository.GetServerName())
nameProperty.AppendXMLSchemaString(data.GetServerName())
person.SetActivityStreamsName(nameProperty)
preferredUsernameProperty := streams.NewActivityStreamsPreferredUsernameProperty()
@@ -121,7 +119,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
person.SetActivityStreamsInbox(inboxProp)
needsFollowApprovalProperty := streams.NewActivityStreamsManuallyApprovesFollowersProperty()
needsFollowApprovalProperty.Set(configRepository.GetFederationIsPrivate())
needsFollowApprovalProperty.Set(data.GetFederationIsPrivate())
person.SetActivityStreamsManuallyApprovesFollowers(needsFollowApprovalProperty)
outboxIRI := MakeLocalIRIForResource("/user/" + accountName + "/outbox")
@@ -154,7 +152,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKeyType)
person.SetW3IDSecurityV1PublicKey(publicKeyProp)
if t, err := configRepository.GetServerInitTime(); t != nil {
if t, err := data.GetServerInitTime(); t != nil {
publishedDateProp := streams.NewActivityStreamsPublishedProperty()
publishedDateProp.Set(t.Time)
person.SetActivityStreamsPublished(publishedDateProp)
@@ -165,8 +163,8 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Profile properties
// Avatar
uniquenessString := configRepository.GetLogoUniquenessString()
userAvatarURLString := configRepository.GetServerURL() + "/logo/external"
uniquenessString := data.GetLogoUniquenessString()
userAvatarURLString := data.GetServerURL() + "/logo/external"
userAvatarURL, err := url.Parse(userAvatarURLString)
userAvatarURL.RawQuery = "uc=" + uniquenessString
if err != nil {
@@ -197,14 +195,14 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Profile bio
summaryProperty := streams.NewActivityStreamsSummaryProperty()
summaryProperty.AppendXMLSchemaString(configRepository.GetServerSummary())
summaryProperty.AppendXMLSchemaString(data.GetServerSummary())
person.SetActivityStreamsSummary(summaryProperty)
// Links
if serverURL := configRepository.GetServerURL(); serverURL != "" {
if serverURL := data.GetServerURL(); serverURL != "" {
addMetadataLinkToProfile(person, "Stream", serverURL)
}
for _, link := range configRepository.GetSocialHandles() {
for _, link := range data.GetSocialHandles() {
addMetadataLinkToProfile(person, link.Platform, link.URL)
}
@@ -222,7 +220,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Tags
tagProp := streams.NewActivityStreamsTagProperty()
for _, tagString := range configRepository.GetServerMetadataTags() {
for _, tagString := range data.GetServerMetadataTags() {
hashtag := MakeHashtag(tagString)
tagProp.AppendTootHashtag(hashtag)
}
@@ -231,7 +229,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService {
// Work around an issue where a single attachment will not serialize
// as an array, so add another item to the mix.
if len(configRepository.GetSocialHandles()) == 1 {
if len(data.GetSocialHandles()) == 1 {
addMetadataLinkToProfile(person, "Owncast", "https://owncast.online")
}

View File

@@ -9,7 +9,6 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
func makeFakeService() vocab.ActivityStreamsService {
@@ -56,11 +55,9 @@ func TestMain(m *testing.M) {
if err != nil {
panic(err)
}
data.SetupPersistence(dbFile.Name())
configRepository := configrepository.Get()
configRepository.SetServerURL("https://my.cool.site.biz")
data.SetServerURL("https://my.cool.site.biz")
m.Run()
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
log "github.com/sirupsen/logrus"
)
@@ -27,9 +27,7 @@ func MakeRemoteIRIForResource(resourcePath string, host string) (*url.URL, error
// MakeLocalIRIForResource will create an IRI for the local server.
func MakeLocalIRIForResource(resourcePath string) *url.URL {
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
host := data.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI url", host, err)
@@ -43,9 +41,7 @@ func MakeLocalIRIForResource(resourcePath string) *url.URL {
// MakeLocalIRIForAccount will return a full IRI for the local server account username.
func MakeLocalIRIForAccount(account string) *url.URL {
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
host := data.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI account server url", err)
@@ -68,9 +64,7 @@ func Serialize(obj vocab.Type) ([]byte, error) {
// MakeLocalIRIForStreamURL will return a full IRI for the local server stream url.
func MakeLocalIRIForStreamURL() *url.URL {
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
host := data.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI stream url", err)
@@ -84,9 +78,7 @@ func MakeLocalIRIForStreamURL() *url.URL {
// MakeLocalIRIforLogo will return a full IRI for the local server logo.
func MakeLocalIRIforLogo() *url.URL {
configRepository := configrepository.Get()
host := configRepository.GetServerURL()
host := data.GetServerURL()
u, err := url.Parse(host)
if err != nil {
log.Errorln("unable to parse local IRI stream url", err)
@@ -101,9 +93,7 @@ func MakeLocalIRIforLogo() *url.URL {
// GetLogoType will return the rel value for the webfinger response and
// the default static image is of type png.
func GetLogoType() string {
configRepository := configrepository.Get()
imageFilename := configRepository.GetLogoPath()
imageFilename := data.GetLogoPath()
if imageFilename == "" {
return "image/png"
}

View File

@@ -9,14 +9,12 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
)
// ActorHandler handles requests for a single actor.
func ActorHandler(w http.ResponseWriter, r *http.Request) {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
if !data.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
@@ -24,7 +22,7 @@ func ActorHandler(w http.ResponseWriter, r *http.Request) {
pathComponents := strings.Split(r.URL.Path, "/")
accountName := pathComponents[3]
if _, valid := configRepository.GetFederatedInboxMap()[accountName]; !valid {
if _, valid := data.GetFederatedInboxMap()[accountName]; !valid {
// User is not valid
w.WriteHeader(http.StatusNotFound)
return

View File

@@ -16,7 +16,7 @@ import (
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
)
const (
@@ -145,9 +145,7 @@ func getFollowersPage(page string, r *http.Request) (vocab.ActivityStreamsOrdere
}
func createPageURL(r *http.Request, page *string) (*url.URL, error) {
configRepository := configrepository.Get()
domain := configRepository.GetServerURL()
domain := data.GetServerURL()
if domain == "" {
return nil, errors.New("unable to get server URL")
}

View File

@@ -7,7 +7,7 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/inbox"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
log "github.com/sirupsen/logrus"
)
@@ -22,9 +22,7 @@ func InboxHandler(w http.ResponseWriter, r *http.Request) {
}
func acceptInboxRequest(w http.ResponseWriter, r *http.Request) {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
if !data.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
@@ -41,7 +39,7 @@ func acceptInboxRequest(w http.ResponseWriter, r *http.Request) {
// The account this request is for must match the account name we have set
// for federation.
if forLocalAccount != configRepository.GetFederationUsername() {
if forLocalAccount != data.GetFederationUsername() {
w.WriteHeader(http.StatusNotFound)
return
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
log "github.com/sirupsen/logrus"
)
@@ -25,14 +25,12 @@ func NodeInfoController(w http.ResponseWriter, r *http.Request) {
Links []links `json:"links"`
}
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
if !data.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
serverURL := configRepository.GetServerURL()
serverURL := data.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return
@@ -91,9 +89,7 @@ func NodeInfoV2Controller(w http.ResponseWriter, r *http.Request) {
Metadata metadata `json:"metadata"`
}
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
if !data.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
@@ -121,7 +117,7 @@ func NodeInfoV2Controller(w http.ResponseWriter, r *http.Request) {
OpenRegistrations: false,
Protocols: []string{"activitypub"},
Metadata: metadata{
ChatEnabled: !configRepository.GetChatDisabled(),
ChatEnabled: !data.GetChatDisabled(),
},
}
@@ -167,14 +163,12 @@ func XNodeInfo2Controller(w http.ResponseWriter, r *http.Request) {
OpenRegistrations bool `json:"openRegistrations"`
}
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
if !data.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
serverURL := configRepository.GetServerURL()
serverURL := data.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return
@@ -184,7 +178,7 @@ func XNodeInfo2Controller(w http.ResponseWriter, r *http.Request) {
res := &response{
Organization: Organization{
Name: configRepository.GetServerName(),
Name: data.GetServerName(),
Contact: serverURL,
},
Server: Server{
@@ -238,14 +232,12 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) {
InvitesEnabled bool `json:"invites_enabled"`
}
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
if !data.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
serverURL := configRepository.GetServerURL()
serverURL := data.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return
@@ -262,9 +254,9 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) {
res := response{
URI: serverURL,
Title: configRepository.GetServerName(),
ShortDescription: configRepository.GetServerSummary(),
Description: configRepository.GetServerSummary(),
Title: data.GetServerName(),
ShortDescription: data.GetServerSummary(),
Description: data.GetServerSummary(),
Version: config.GetReleaseString(),
Stats: Stats{
UserCount: 1,
@@ -283,9 +275,7 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) {
}
func writeResponse(payload interface{}, w http.ResponseWriter) error {
configRepository := configrepository.Get()
accountName := configRepository.GetDefaultFederationUsername()
accountName := data.GetDefaultFederationUsername()
actorIRI := apmodels.MakeLocalIRIForAccount(accountName)
publicKey := crypto.GetPublicKey(actorIRI)
@@ -294,15 +284,7 @@ func writeResponse(payload interface{}, w http.ResponseWriter) error {
// HostMetaController points to webfinger.
func HostMetaController(w http.ResponseWriter, r *http.Request) {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
log.Debugln("host meta request rejected! Federation is not enabled")
return
}
serverURL := configRepository.GetServerURL()
serverURL := data.GetServerURL()
if serverURL == "" {
w.WriteHeader(http.StatusNotFound)
return

View File

@@ -8,33 +8,31 @@ import (
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
log "github.com/sirupsen/logrus"
)
// ObjectHandler handles requests for a single federated ActivityPub object.
func ObjectHandler(w http.ResponseWriter, r *http.Request) {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
if !data.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
// If private federation mode is enabled do not allow access to objects.
if configRepository.GetFederationIsPrivate() {
if data.GetFederationIsPrivate() {
w.WriteHeader(http.StatusNotFound)
return
}
iri := strings.Join([]string{strings.TrimSuffix(configRepository.GetServerURL(), "/"), r.URL.Path}, "")
iri := strings.Join([]string{strings.TrimSuffix(data.GetServerURL(), "/"), r.URL.Path}, "")
object, _, _, err := persistence.GetObjectByIRI(iri)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
accountName := configRepository.GetDefaultFederationUsername()
accountName := data.GetDefaultFederationUsername()
actorIRI := apmodels.MakeLocalIRIForAccount(accountName)
publicKey := crypto.GetPublicKey(actorIRI)

View File

@@ -6,22 +6,20 @@ import (
"strings"
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
// WebfingerHandler will handle webfinger lookup requests.
func WebfingerHandler(w http.ResponseWriter, r *http.Request) {
configRepository := configrepository.Get()
if !configRepository.GetFederationEnabled() {
if !data.GetFederationEnabled() {
w.WriteHeader(http.StatusMethodNotAllowed)
log.Debugln("webfinger request rejected! Federation is not enabled")
return
}
instanceHostURL := configRepository.GetServerURL()
instanceHostURL := data.GetServerURL()
if instanceHostURL == "" {
w.WriteHeader(http.StatusNotFound)
log.Warnln("webfinger request rejected! Federation is enabled but server URL is empty.")
@@ -31,7 +29,7 @@ func WebfingerHandler(w http.ResponseWriter, r *http.Request) {
instanceHostString := utils.GetHostnameFromURLString(instanceHostURL)
if instanceHostString == "" {
w.WriteHeader(http.StatusNotFound)
log.Warnln("webfinger request rejected! Federation is enabled but server URL is not set properly. data.GetServerURL(): " + configRepository.GetServerURL())
log.Warnln("webfinger request rejected! Federation is enabled but server URL is not set properly. data.GetServerURL(): " + data.GetServerURL())
return
}
@@ -53,7 +51,7 @@ func WebfingerHandler(w http.ResponseWriter, r *http.Request) {
host := userComponents[1]
user := userComponents[0]
if _, valid := configRepository.GetFederatedInboxMap()[user]; !valid {
if _, valid := data.GetFederatedInboxMap()[user]; !valid {
w.WriteHeader(http.StatusNotFound)
log.Debugln("webfinger request rejected! Invalid user: " + user)
return

View File

@@ -8,15 +8,13 @@ import (
"errors"
"net/url"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
log "github.com/sirupsen/logrus"
)
// GetPublicKey will return the public key for the provided actor.
func GetPublicKey(actorIRI *url.URL) PublicKey {
configRepository := configrepository.Get()
key := configRepository.GetPublicKey()
key := data.GetPublicKey()
idURL, err := url.Parse(actorIRI.String() + "#main-key")
if err != nil {
log.Errorln("unable to parse actor iri string", idURL, err)
@@ -31,9 +29,7 @@ func GetPublicKey(actorIRI *url.URL) PublicKey {
// GetPrivateKey will return the internal server private key.
func GetPrivateKey() *rsa.PrivateKey {
configRepository := configrepository.Get()
key := configRepository.GetPrivateKey()
key := data.GetPrivateKey()
block, _ := pem.Decode([]byte(key))
if block == nil {

View File

@@ -7,19 +7,17 @@ import (
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
)
func handleEngagementActivity(eventType events.EventType, isLiveNotification bool, actorReference vocab.ActivityStreamsActorProperty, action string) error {
configRepository := configrepository.Get()
// Do nothing if displaying engagement actions has been turned off.
if !configRepository.GetFederationShowEngagement() {
if !data.GetFederationShowEngagement() {
return nil
}
// Do nothing if chat is disabled
if configRepository.GetChatDisabled() {
if data.GetChatDisabled() {
return nil
}
@@ -38,11 +36,11 @@ func handleEngagementActivity(eventType events.EventType, isLiveNotification boo
if isLiveNotification && action == events.FediverseEngagementLike {
suffix = "liked that this stream went live."
} else if action == events.FediverseEngagementLike {
suffix = fmt.Sprintf("liked a post from %s.", configRepository.GetServerName())
suffix = fmt.Sprintf("liked a post from %s.", data.GetServerName())
} else if isLiveNotification && action == events.FediverseEngagementRepost {
suffix = "shared this stream with their followers."
} else if action == events.FediverseEngagementRepost {
suffix = fmt.Sprintf("shared a post from %s.", configRepository.GetServerName())
suffix = fmt.Sprintf("shared a post from %s.", data.GetServerName())
} else if action == events.FediverseEngagementFollow {
suffix = "followed this stream."
} else {

View File

@@ -10,15 +10,13 @@ import (
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsFollow) error {
configRepository := configrepository.Get()
follow, err := resolvers.MakeFollowRequest(c, activity)
if err != nil {
log.Errorln("unable to create follow inbox request", err)
@@ -29,7 +27,7 @@ func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsF
return fmt.Errorf("unable to handle request")
}
approved := !configRepository.GetFederationIsPrivate()
approved := !data.GetFederationIsPrivate()
followRequest := *follow
@@ -38,7 +36,7 @@ func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsF
return err
}
localAccountName := configRepository.GetDefaultFederationUsername()
localAccountName := data.GetDefaultFederationUsername()
if approved {
if err := requests.SendFollowAccept(follow.Inbox, activity, localAccountName); err != nil {

View File

@@ -15,7 +15,7 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
log "github.com/sirupsen/logrus"
)
@@ -131,9 +131,7 @@ func Verify(request *http.Request) (bool, error) {
}
func isBlockedDomain(domain string) bool {
configRepository := configrepository.Get()
blockedDomains := configRepository.GetBlockedFederatedDomains()
blockedDomains := data.GetBlockedFederatedDomains()
for _, blockedDomain := range blockedDomains {
if strings.Contains(domain, blockedDomain) {

View File

@@ -9,7 +9,6 @@ import (
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/persistence/configrepository"
)
func makeFakePerson() vocab.ActivityStreamsPerson {
@@ -50,24 +49,21 @@ func makeFakePerson() vocab.ActivityStreamsPerson {
func TestMain(m *testing.M) {
data.SetupPersistence(":memory:")
configRepository := configrepository.Get()
configRepository.SetServerURL("https://my.cool.site.biz")
data.SetServerURL("https://my.cool.site.biz")
persistence.Setup(data.GetDatastore())
m.Run()
}
func TestBlockedDomains(t *testing.T) {
configRepository := configrepository.Get()
person := makeFakePerson()
configRepository.SetBlockedFederatedDomains([]string{"freedom.eagle", "guns.life"})
data.SetBlockedFederatedDomains([]string{"freedom.eagle", "guns.life"})
if len(configRepository.GetBlockedFederatedDomains()) != 2 {
if len(data.GetBlockedFederatedDomains()) != 2 {
t.Error("Blocked federated domains is not set correctly")
}
for _, domain := range configRepository.GetBlockedFederatedDomains() {
for _, domain := range data.GetBlockedFederatedDomains() {
if domain == person.GetJSONLDId().GetIRI().Host {
return
}

View File

@@ -16,10 +16,10 @@ import (
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/activitypub/webfinger"
"github.com/owncast/owncast/activitypub/workerpool"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/pkg/errors"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
"github.com/teris-io/shortid"
@@ -27,9 +27,7 @@ import (
// SendLive will send all followers the message saying you started a live stream.
func SendLive() error {
configRepository := configrepository.Get()
textContent := configRepository.GetFederationGoLiveMessage()
textContent := data.GetFederationGoLiveMessage()
// If the message is empty then do not send it.
if textContent == "" {
@@ -40,7 +38,7 @@ func SendLive() error {
reg := regexp.MustCompile("[^a-zA-Z0-9]+")
tagProp := streams.NewActivityStreamsTagProperty()
for _, tagString := range configRepository.GetServerMetadataTags() {
for _, tagString := range data.GetServerMetadataTags() {
tagWithoutSpecialCharacters := reg.ReplaceAllString(tagString, "")
hashtag := apmodels.MakeHashtag(tagWithoutSpecialCharacters)
tagProp.AppendTootHashtag(hashtag)
@@ -59,15 +57,15 @@ func SendLive() error {
tagsString := strings.Join(tagStrings, " ")
var streamTitle string
if title := configRepository.GetStreamTitle(); title != "" {
if title := data.GetStreamTitle(); title != "" {
streamTitle = fmt.Sprintf("<p>%s</p>", title)
}
textContent = fmt.Sprintf("<p>%s</p>%s<p>%s</p><p><a href=\"%s\">%s</a></p>", textContent, streamTitle, tagsString, configRepository.GetServerURL(), configRepository.GetServerURL())
textContent = fmt.Sprintf("<p>%s</p>%s<p>%s</p><p><a href=\"%s\">%s</a></p>", textContent, streamTitle, tagsString, data.GetServerURL(), data.GetServerURL())
activity, _, note, noteID := createBaseOutboundMessage(textContent)
// To the public if we're not treating ActivityPub as "private".
if !configRepository.GetFederationIsPrivate() {
if !data.GetFederationIsPrivate() {
note = apmodels.MakeNotePublic(note)
activity = apmodels.MakeActivityPublic(activity)
}
@@ -75,7 +73,7 @@ func SendLive() error {
note.SetActivityStreamsTag(tagProp)
// Attach an image along with the Federated message.
previewURL, err := url.Parse(configRepository.GetServerURL())
previewURL, err := url.Parse(data.GetServerURL())
if err == nil {
var imageToAttach string
var mediaType string
@@ -96,7 +94,7 @@ func SendLive() error {
}
}
if configRepository.GetNSFW() {
if data.GetNSFW() {
// Mark content as sensitive.
sensitive := streams.NewActivityStreamsSensitiveProperty()
sensitive.AppendXMLSchemaBoolean(true)
@@ -153,8 +151,6 @@ func SendDirectMessageToAccount(textContent, account string) error {
// SendPublicMessage will send a public message to all followers.
func SendPublicMessage(textContent string) error {
configRepository := configrepository.Get()
originalContent := textContent
textContent = utils.RenderSimpleMarkdown(textContent)
@@ -177,7 +173,7 @@ func SendPublicMessage(textContent string) error {
activity, _, note, noteID := createBaseOutboundMessage(textContent)
note.SetActivityStreamsTag(tagProp)
if !configRepository.GetFederationIsPrivate() {
if !data.GetFederationIsPrivate() {
note = apmodels.MakeNotePublic(note)
activity = apmodels.MakeActivityPublic(activity)
}
@@ -201,8 +197,7 @@ func SendPublicMessage(textContent string) error {
// nolint: unparam
func createBaseOutboundMessage(textContent string) (vocab.ActivityStreamsCreate, string, vocab.ActivityStreamsNote, string) {
configRepository := configrepository.Get()
localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
noteID := shortid.MustGenerate()
noteIRI := apmodels.MakeLocalIRIForResource(noteID)
id := shortid.MustGenerate()
@@ -223,8 +218,7 @@ func getHashtagLinkHTMLFromTagString(baseHashtag string) string {
// SendToFollowers will send an arbitrary payload to all follower inboxes.
func SendToFollowers(payload []byte) error {
configRepository := configrepository.Get()
localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
followers, _, err := persistence.GetFederationFollowers(-1, 0)
if err != nil {
@@ -247,8 +241,7 @@ func SendToFollowers(payload []byte) error {
// SendToUser will send a payload to a single specific inbox.
func SendToUser(inbox *url.URL, payload []byte) error {
configRepository := configrepository.Get()
localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
req, err := requests.CreateSignedRequest(payload, inbox, localActor)
if err != nil {
@@ -262,10 +255,8 @@ func SendToUser(inbox *url.URL, payload []byte) error {
// UpdateFollowersWithAccountUpdates will send an update to all followers alerting of a profile update.
func UpdateFollowersWithAccountUpdates() error {
configRepository := configrepository.Get()
// Don't do anything if federation is disabled.
if !configRepository.GetFederationEnabled() {
if !data.GetFederationEnabled() {
return nil
}
@@ -274,7 +265,7 @@ func UpdateFollowersWithAccountUpdates() error {
activity := apmodels.MakeUpdateActivity(objectID)
actor := streams.NewActivityStreamsPerson()
actorID := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
actorID := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
actorIDProperty := streams.NewJSONLDIdProperty()
actorIDProperty.Set(actorID)
actor.SetJSONLDId(actorIDProperty)

View File

@@ -45,8 +45,8 @@ func GetFederationFollowers(limit int, offset int) ([]models.Follower, int, erro
}
followersResult, err := _datastore.GetQueries().GetFederationFollowersWithOffset(ctx, db.GetFederationFollowersWithOffsetParams{
Limit: limit,
Offset: offset,
Limit: int32(limit),
Offset: int32(offset),
})
if err != nil {
return nil, 0, err

View File

@@ -237,7 +237,7 @@ func GetOutbox(limit int, offset int) (vocab.ActivityStreamsOrderedCollection, e
orderedItems := streams.NewActivityStreamsOrderedItemsProperty()
rows, err := _datastore.GetQueries().GetOutboxWithOffset(
context.Background(),
db.GetOutboxWithOffsetParams{Limit: limit, Offset: offset},
db.GetOutboxWithOffsetParams{Limit: int32(limit), Offset: int32(offset)},
)
if err != nil {
return collection, err
@@ -309,8 +309,8 @@ func SaveInboundFediverseActivity(objectIRI string, actorIRI string, eventType s
func GetInboundActivities(limit int, offset int) ([]models.FederatedActivity, int, error) {
ctx := context.Background()
rows, err := _datastore.GetQueries().GetInboundActivitiesWithOffset(ctx, db.GetInboundActivitiesWithOffsetParams{
Limit: limit,
Offset: offset,
Limit: int32(limit),
Offset: int32(offset),
})
if err != nil {
return nil, 0, err

View File

@@ -10,7 +10,7 @@ import (
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/crypto"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
@@ -47,12 +47,11 @@ func Resolve(c context.Context, data []byte, callbacks ...interface{}) error {
// ResolveIRI will resolve an IRI ahd call the correct callback for the resolved type.
func ResolveIRI(c context.Context, iri string, callbacks ...interface{}) error {
configRepository := configrepository.Get()
log.Debugln("Resolving", iri)
req, _ := http.NewRequest(http.MethodGet, iri, nil)
actor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername())
actor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername())
if err := crypto.SignRequest(req, nil, actor); err != nil {
return err
}

View File

@@ -2,10 +2,14 @@ package workerpool
import (
"net/http"
"runtime"
log "github.com/sirupsen/logrus"
)
// workerPoolSize defines the number of concurrent HTTP ActivityPub requests.
var workerPoolSize = runtime.GOMAXPROCS(0)
// Job struct bundling the ActivityPub and the payload in one struct.
type Job struct {
request *http.Request
@@ -14,8 +18,8 @@ type Job struct {
var queue chan Job
// InitOutboundWorkerPool starts n go routines that await ActivityPub jobs.
func InitOutboundWorkerPool(workerPoolSize int) {
queue = make(chan Job, workerPoolSize)
func InitOutboundWorkerPool() {
queue = make(chan Job)
// start workers
for i := 1; i <= workerPoolSize; i++ {
@@ -25,13 +29,8 @@ func InitOutboundWorkerPool(workerPoolSize int) {
// AddToOutboundQueue will queue up an outbound http request.
func AddToOutboundQueue(req *http.Request) {
select {
case queue <- Job{req}:
default:
log.Debugln("Outbound ActivityPub job queue is full")
queue <- Job{req} // will block until received by a worker at this point
}
log.Tracef("Queued request for ActivityPub destination %s", req.RequestURI)
queue <- Job{req}
}
func worker(workerID int, queue <-chan Job) {

View File

@@ -11,7 +11,7 @@ import (
"sync"
"time"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/utils"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
@@ -47,8 +47,6 @@ func setupExpiredRequestPruner() {
// StartAuthFlow will begin the IndieAuth flow by generating an auth request.
func StartAuthFlow(authHost, userID, accessToken, displayName string) (*url.URL, error) {
configRepository := configrepository.Get()
// Limit the number of pending requests
if len(pendingAuthRequests) >= maxPendingRequests {
return nil, errors.New("Please try again later. Too many pending requests.")
@@ -70,7 +68,7 @@ func StartAuthFlow(authHost, userID, accessToken, displayName string) (*url.URL,
return nil, errors.New("only servers secured with https are supported")
}
serverURL := configRepository.GetServerURL()
serverURL := data.GetServerURL()
if serverURL == "" {
return nil, errors.New("Owncast server URL must be set when using auth")
}

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
"github.com/pkg/errors"
"github.com/teris-io/shortid"
)
@@ -70,8 +70,6 @@ func StartServerAuth(clientID, redirectURI, codeChallenge, state, me string) (*S
// CompleteServerAuth will verify that the values provided in the final step
// of the IndieAuth flow are correct, and return some basic profile info.
func CompleteServerAuth(code, redirectURI, clientID string, codeVerifier string) (*ServerProfileResponse, error) {
configRepository := configrepository.Get()
request, pending := pendingServerAuthRequests[code]
if !pending {
return nil, errors.New("no pending authentication request")
@@ -91,11 +89,11 @@ func CompleteServerAuth(code, redirectURI, clientID string, codeVerifier string)
}
response := ServerProfileResponse{
Me: configRepository.GetServerURL(),
Me: data.GetServerURL(),
Profile: ServerProfile{
Name: configRepository.GetServerName(),
URL: configRepository.GetServerURL(),
Photo: fmt.Sprintf("%s/%s", configRepository.GetServerURL(), configRepository.GetLogoPath()),
Name: data.GetServerName(),
URL: data.GetServerURL(),
Photo: fmt.Sprintf("%s/%s", data.GetServerURL(), data.GetLogoPath()),
},
}

View File

@@ -8,8 +8,8 @@ folderPath="webserver/handlers/generated"
specPath="openapi.yaml"
# validate scripts are installed
if ! command -v redocly &>/dev/null; then
echo "Please install \`redocly cli\` before running this script: npm install -g @redocly/cli"
if ! command -v swagger-cli &>/dev/null; then
echo "Please install \`swagger-cli\` before running this script"
exit 1
fi
@@ -20,7 +20,7 @@ if ! command -v oapi-codegen &>/dev/null; then
fi
# validate schema
npx redocly lint $specPath
swagger-cli validate $specPath
if [ $? -ne 0 ]; then
echo "Open API specification is not valid"
exit 1

View File

@@ -4,7 +4,7 @@ import "path/filepath"
const (
// StaticVersionNumber is the version of Owncast that is used when it's not overwritten via build-time settings.
StaticVersionNumber = "0.2.2" // Shown when you build from develop
StaticVersionNumber = "0.2.0" // Shown when you build from develop
// FfmpegSuggestedVersion is the version of ffmpeg we suggest.
FfmpegSuggestedVersion = "v4.1.5" // Requires the v
// DataDirectory is the directory we save data to.

View File

@@ -7,8 +7,8 @@ import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
log "github.com/sirupsen/logrus"
@@ -23,8 +23,6 @@ var (
func Start(getStatusFunc func() models.Status) error {
setupPersistence()
configRepository := configrepository.Get()
getStatus = getStatusFunc
_server = NewChat()
@@ -37,7 +35,7 @@ func Start(getStatusFunc func() models.Status) error {
Help: "The number of chat messages incremented over time.",
ConstLabels: map[string]string{
"version": config.VersionNumber,
"host": configRepository.GetServerURL(),
"host": data.GetServerURL(),
},
})

View File

@@ -13,8 +13,8 @@ import (
"github.com/gorilla/websocket"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/services/geoip"
)
@@ -133,9 +133,7 @@ func (c *Client) readPump() {
}
// Check if this message passes the optional language filter
configRepository := configrepository.Get()
if configRepository.GetChatSlurFilterEnabled() && !c.messageFilter.Allow(string(message)) {
if data.GetChatSlurFilterEnabled() && !c.messageFilter.Allow(string(message)) {
c.sendAction("Sorry, that message contained language that is not allowed in this chat.")
continue
}
@@ -211,11 +209,9 @@ func (c *Client) close() {
}
func (c *Client) passesRateLimit() bool {
configRepository := configrepository.Get()
// If spam rate limiting is disabled, or the user is a moderator, always
// allow the message.
if !configRepository.GetChatSpamProtectionEnabled() || c.User.IsModerator() {
if !data.GetChatSpamProtectionEnabled() || c.User.IsModerator() {
return true
}

View File

@@ -10,14 +10,14 @@ import (
log "github.com/sirupsen/logrus"
)
func setSystemConcurrentConnectionLimit(limit uint64) {
func setSystemConcurrentConnectionLimit(limit int64) {
var rLimit syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
log.Fatalln(err)
}
originalLimit := rLimit.Cur
rLimit.Cur = limit
rLimit.Cur = uint64(limit)
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
log.Fatalln(err)
}

View File

@@ -9,7 +9,7 @@ import (
log "github.com/sirupsen/logrus"
)
func setSystemConcurrentConnectionLimit(limit uint64) {
func setSystemConcurrentConnectionLimit(limit int64) {
var rLimit syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
log.Fatalln(err)

View File

@@ -3,4 +3,4 @@
package chat
func setSystemConcurrentConnectionLimit(limit uint64) {}
func setSystemConcurrentConnectionLimit(limit int64) {}

View File

@@ -8,8 +8,8 @@ import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
@@ -22,12 +22,10 @@ func (s *Server) userNameChanged(eventData chatClientEvent) {
return
}
configRepository := configrepository.Get()
proposedUsername := receivedEvent.NewName
// Check if name is on the blocklist
blocklist := configRepository.GetForbiddenUsernameList()
blocklist := data.GetForbiddenUsernameList()
// Names have a max length
proposedUsername = utils.MakeSafeStringOfLength(proposedUsername, config.MaxChatDisplayNameLength)

View File

@@ -17,7 +17,7 @@ import (
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/util"
"mvdan.cc/xurls/v2"
"mvdan.cc/xurls"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
@@ -220,7 +220,7 @@ func RenderMarkdown(raw string) string {
[]byte("https:"),
}),
extension.WithLinkifyURLRegexp(
xurls.Strict(),
xurls.Strict,
),
),
emoji.New(

View File

@@ -1,8 +1,6 @@
package events
import (
"github.com/owncast/owncast/persistence/configrepository"
)
import "github.com/owncast/owncast/core/data"
// FediverseEngagementEvent is a message displayed in chat on representing an action on the Fediverse.
type FediverseEngagementEvent struct {
@@ -15,8 +13,6 @@ type FediverseEngagementEvent struct {
// GetBroadcastPayload will return the object to send to all chat users.
func (e *FediverseEngagementEvent) GetBroadcastPayload() EventPayload {
configRepository := configrepository.Get()
return EventPayload{
"id": e.ID,
"timestamp": e.Timestamp,
@@ -26,7 +22,7 @@ func (e *FediverseEngagementEvent) GetBroadcastPayload() EventPayload {
"title": e.UserAccountName,
"link": e.Link,
"user": EventPayload{
"displayName": configRepository.GetServerName(),
"displayName": data.GetServerName(),
},
}
}

View File

@@ -1,8 +1,6 @@
package events
import (
"github.com/owncast/owncast/persistence/configrepository"
)
import "github.com/owncast/owncast/core/data"
// SystemMessageEvent is a message displayed in chat on behalf of the server.
type SystemMessageEvent struct {
@@ -12,15 +10,13 @@ type SystemMessageEvent struct {
// GetBroadcastPayload will return the object to send to all chat users.
func (e *SystemMessageEvent) GetBroadcastPayload() EventPayload {
configRepository := configrepository.Get()
return EventPayload{
"id": e.ID,
"timestamp": e.Timestamp,
"body": e.Body,
"type": SystemMessageSent,
"user": EventPayload{
"displayName": configRepository.GetServerName(),
"displayName": data.GetServerName(),
},
}
}

View File

@@ -9,7 +9,6 @@ import (
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/authrepository"
"github.com/owncast/owncast/persistence/tables"
log "github.com/sirupsen/logrus"
@@ -25,9 +24,7 @@ const (
func setupPersistence() {
_datastore = data.GetDatastore()
tables.CreateMessagesTable(_datastore.DB)
authRepository := authrepository.Get()
authRepository.CreateBanIPTable(_datastore.DB)
data.CreateBanIPTable(_datastore.DB)
chatDataPruner := time.NewTicker(5 * time.Minute)
go func() {

View File

@@ -13,10 +13,9 @@ import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/authrepository"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/services/geoip"
"github.com/owncast/owncast/utils"
@@ -42,7 +41,7 @@ type Server struct {
// a map of user IDs and timers that fire for chat part messages.
userPartedTimers map[string]*time.Ticker
seq uint
maxSocketConnectionLimit uint64
maxSocketConnectionLimit int64
mu sync.RWMutex
}
@@ -96,9 +95,7 @@ func (s *Server) Addclient(conn *websocket.Conn, user *models.User, accessToken
ConnectedAt: time.Now(),
}
configRepository := configrepository.Get()
shouldSendJoinedMessages := configRepository.GetChatJoinPartMessagesEnabled()
shouldSendJoinedMessages := data.GetChatJoinPartMessagesEnabled()
// If there are existing clients connected for this user do not send
// a user joined message. Do not put this under a mutex, as
@@ -189,10 +186,8 @@ func (s *Server) sendUserPartedMessage(c *Client) {
userPartEvent.User = c.User
userPartEvent.ClientID = c.Id
configRepository := configrepository.Get()
// If part messages are disabled.
if configRepository.GetChatJoinPartMessagesEnabled() {
if data.GetChatJoinPartMessagesEnabled() {
if err := s.Broadcast(userPartEvent.GetBroadcastPayload()); err != nil {
log.Errorln("error sending chat part message", err)
}
@@ -203,17 +198,14 @@ func (s *Server) sendUserPartedMessage(c *Client) {
// HandleClientConnection is fired when a single client connects to the websocket.
func (s *Server) HandleClientConnection(w http.ResponseWriter, r *http.Request) {
configRepository := configrepository.Get()
authRepository := authrepository.Get()
if configRepository.GetChatDisabled() {
if data.GetChatDisabled() {
_, _ = w.Write([]byte(events.ChatDisabled))
return
}
ipAddress := utils.GetIPAddressFromRequest(r)
// Check if this client's IP address is banned. If so send a rejection.
if blocked, err := authRepository.IsIPAddressBanned(ipAddress); blocked {
if blocked, err := data.IsIPAddressBanned(ipAddress); blocked {
log.Debugln("Client ip address has been blocked. Rejecting.")
w.WriteHeader(http.StatusForbidden)
@@ -223,7 +215,7 @@ func (s *Server) HandleClientConnection(w http.ResponseWriter, r *http.Request)
}
// Limit concurrent chat connections
if uint64(len(s.clients)) >= s.maxSocketConnectionLimit {
if int64(len(s.clients)) >= s.maxSocketConnectionLimit {
log.Warnln("rejecting incoming client connection as it exceeds the max client count of", s.maxSocketConnectionLimit)
_, _ = w.Write([]byte(events.ErrorMaxConnectionsExceeded))
return
@@ -385,14 +377,12 @@ func SendActionToUser(userID string, text string) error {
}
func (s *Server) eventReceived(event chatClientEvent) {
configRepository := configrepository.Get()
c := event.client
u := c.User
// If established chat user only mode is enabled and the user is not old
// enough then reject this event and send them an informative message.
if u != nil && configRepository.GetChatEstbalishedUsersOnlyMode() && time.Since(event.client.User.CreatedAt) < config.GetDefaults().ChatEstablishedUserModeTimeDuration && !u.IsModerator() {
if u != nil && data.GetChatEstbalishedUsersOnlyMode() && time.Since(event.client.User.CreatedAt) < config.GetDefaults().ChatEstablishedUserModeTimeDuration && !u.IsModerator() {
s.sendActionToClient(c, "You have not been an established chat participant long enough to take part in chat. Please enjoy the stream and try again later.")
return
}
@@ -419,12 +409,10 @@ func (s *Server) eventReceived(event chatClientEvent) {
}
func (s *Server) sendWelcomeMessageToClient(c *Client) {
configRepository := configrepository.Get()
// Add an artificial delay so people notice this message come in.
time.Sleep(7 * time.Second)
welcomeMessage := utils.RenderSimpleMarkdown(configRepository.GetServerWelcomeMessage())
welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage())
if welcomeMessage != "" {
s.sendSystemMessageToClient(c, welcomeMessage)
@@ -432,9 +420,7 @@ func (s *Server) sendWelcomeMessageToClient(c *Client) {
}
func (s *Server) sendAllWelcomeMessage() {
configRepository := configrepository.Get()
welcomeMessage := utils.RenderSimpleMarkdown(configRepository.GetServerWelcomeMessage())
welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage())
if welcomeMessage != "" {
clientMessage := events.SystemMessageEvent{

View File

@@ -9,14 +9,14 @@ import (
log "github.com/sirupsen/logrus"
)
func getMaximumConcurrentConnectionLimit() uint64 {
func getMaximumConcurrentConnectionLimit() int64 {
var rLimit syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
log.Fatalln(err)
}
// Return the limit to 70% of max so the machine doesn't die even if it's maxed out for some reason.
proposedLimit := uint64(float32(rLimit.Max) * 0.7)
proposedLimit := int64(float32(rLimit.Max) * 0.7)
return proposedLimit
}

View File

@@ -3,7 +3,7 @@
package chat
func getMaximumConcurrentConnectionLimit() uint64 {
func getMaximumConcurrentConnectionLimit() int64 {
// The maximum limit I can find for windows is 16,777,216
// (essentially unlimited, but add the 0.7 multiplier as well to be
// consistent with other systems)

View File

@@ -16,7 +16,6 @@ import (
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/notifications"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/persistence/tables"
"github.com/owncast/owncast/utils"
"github.com/owncast/owncast/yp"
@@ -35,10 +34,10 @@ var (
// Start starts up the core processing.
func Start() error {
resetDirectories()
configRepository := configrepository.Get()
// configRepository.PopulateDefaults()
if err := configRepository.VerifySettings(); err != nil {
data.PopulateDefaults()
if err := data.VerifySettings(); err != nil {
log.Error(err)
return err
}
@@ -76,7 +75,7 @@ func Start() error {
// start the rtmp server
go rtmp.Start(setStreamAsConnected, setBroadcaster)
rtmpPort := configRepository.GetRTMPPortNumber()
rtmpPort := data.GetRTMPPortNumber()
if rtmpPort != 1935 {
log.Infof("RTMP is accepting inbound streams on port %d.", rtmpPort)
}
@@ -114,8 +113,7 @@ func transitionToOfflineVideoStreamContent() {
go _transcoder.Start(false)
// Copy the logo to be the thumbnail
configRepository := configrepository.Get()
logo := configRepository.GetLogoPath()
logo := data.GetLogoPath()
dst := filepath.Join(config.TempDir, "thumbnail.jpg")
if err = utils.Copy(filepath.Join("data", logo), dst); err != nil {
log.Warnln(err)
@@ -132,8 +130,7 @@ func resetDirectories() {
utils.CleanupDirectory(config.HLSStoragePath)
// Remove the previous thumbnail
configRepository := configrepository.Get()
logo := configRepository.GetLogoPath()
logo := data.GetLogoPath()
if utils.DoesFileExists(logo) {
err := utils.Copy(path.Join("data", logo), filepath.Join(config.DataDirectory, "thumbnail.jpg"))
if err != nil {

13
core/data/activitypub.go Normal file
View File

@@ -0,0 +1,13 @@
package data
// GetFederatedInboxMap is a mapping between account names and their outbox.
func GetFederatedInboxMap() map[string]string {
return map[string]string{
GetDefaultFederationUsername(): GetDefaultFederationUsername(),
}
}
// GetDefaultFederationUsername will return the username used for sending federation activities.
func GetDefaultFederationUsername() string {
return GetFederationUsername()
}

1027
core/data/config.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
package models
package data
import (
"bytes"
@@ -12,48 +12,48 @@ type ConfigEntry struct {
Key string
}
func (c *ConfigEntry) GetStringSlice() ([]string, error) {
decoder := c.GetDecoder()
func (c *ConfigEntry) getStringSlice() ([]string, error) {
decoder := c.getDecoder()
var result []string
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) GetStringMap() (map[string]string, error) {
decoder := c.GetDecoder()
func (c *ConfigEntry) getStringMap() (map[string]string, error) {
decoder := c.getDecoder()
var result map[string]string
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) GetString() (string, error) {
decoder := c.GetDecoder()
func (c *ConfigEntry) getString() (string, error) {
decoder := c.getDecoder()
var result string
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) GetNumber() (float64, error) {
decoder := c.GetDecoder()
func (c *ConfigEntry) getNumber() (float64, error) {
decoder := c.getDecoder()
var result float64
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) GetBool() (bool, error) {
decoder := c.GetDecoder()
func (c *ConfigEntry) getBool() (bool, error) {
decoder := c.getDecoder()
var result bool
err := decoder.Decode(&result)
return result, err
}
func (c *ConfigEntry) GetObject(result interface{}) error {
decoder := c.GetDecoder()
func (c *ConfigEntry) getObject(result interface{}) error {
decoder := c.getDecoder()
err := decoder.Decode(result)
return err
}
func (c *ConfigEntry) GetDecoder() *gob.Decoder {
func (c *ConfigEntry) getDecoder() *gob.Decoder {
valueBytes := c.Value.([]byte)
decoder := gob.NewDecoder(bytes.NewBuffer(valueBytes))
return decoder

23
core/data/crypto.go Normal file
View File

@@ -0,0 +1,23 @@
package data
// GetPublicKey will return the public key.
func GetPublicKey() string {
value, _ := _datastore.GetString(publicKeyKey)
return value
}
// SetPublicKey will save the public key.
func SetPublicKey(key string) error {
return _datastore.SetString(publicKeyKey, key)
}
// GetPrivateKey will return the private key.
func GetPrivateKey() string {
value, _ := _datastore.GetString(privateKeyKey)
return value
}
// SetPrivateKey will save the private key.
func SetPrivateKey(key string) error {
return _datastore.SetString(privateKeyKey, key)
}

View File

@@ -4,8 +4,6 @@ import (
"fmt"
"os"
"testing"
"github.com/owncast/owncast/models"
)
func TestMain(m *testing.M) {
@@ -91,7 +89,7 @@ func TestCustomType(t *testing.T) {
}
// Save config entry to the database
if err := _datastore.Save(models.ConfigEntry{&testStruct, testKey}); err != nil {
if err := _datastore.Save(ConfigEntry{&testStruct, testKey}); err != nil {
t.Error(err)
}
@@ -103,7 +101,7 @@ func TestCustomType(t *testing.T) {
// Get a typed struct out of it
var testResult TestStruct
if err := entryResult.GetObject(&testResult); err != nil {
if err := entryResult.getObject(&testResult); err != nil {
t.Error(err)
}
@@ -123,7 +121,7 @@ func TestStringMap(t *testing.T) {
}
// Save config entry to the database
if err := _datastore.Save(models.ConfigEntry{Value: &testMap, Key: testKey}); err != nil {
if err := _datastore.Save(ConfigEntry{&testMap, testKey}); err != nil {
t.Error(err)
}
@@ -133,7 +131,7 @@ func TestStringMap(t *testing.T) {
t.Error(err)
}
testResult, err := entryResult.GetStringMap()
testResult, err := entryResult.getStringMap()
if err != nil {
t.Error(err)
}

View File

@@ -1,9 +1,8 @@
package configrepository
package data
import (
"strings"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus"
)
@@ -13,8 +12,8 @@ const (
datastoreValueVersionKey = "DATA_STORE_VERSION"
)
func migrateDatastoreValues(datastore *data.Datastore, configRepository ConfigRepository) {
currentVersion, _ := datastore.GetNumber(datastoreValueVersionKey)
func migrateDatastoreValues(datastore *Datastore) {
currentVersion, _ := _datastore.GetNumber(datastoreValueVersionKey)
if currentVersion == 0 {
currentVersion = datastoreValuesVersion
}
@@ -25,21 +24,21 @@ func migrateDatastoreValues(datastore *data.Datastore, configRepository ConfigRe
case 0:
migrateToDatastoreValues1(datastore)
case 1:
migrateToDatastoreValues2(datastore, configRepository)
migrateToDatastoreValues2(datastore)
case 2:
migrateToDatastoreValues3ServingEndpoint3(configRepository)
migrateToDatastoreValues3ServingEndpoint3(datastore)
case 3:
migrateToDatastoreValues4(datastore, configRepository)
migrateToDatastoreValues4(datastore)
default:
log.Fatalln("missing datastore values migration step")
}
}
if err := datastore.SetNumber(datastoreValueVersionKey, datastoreValuesVersion); err != nil {
if err := _datastore.SetNumber(datastoreValueVersionKey, datastoreValuesVersion); err != nil {
log.Errorln("error setting datastore value version:", err)
}
}
func migrateToDatastoreValues1(datastore *data.Datastore) {
func migrateToDatastoreValues1(datastore *Datastore) {
// Migrate the forbidden usernames to be a slice instead of a string.
forbiddenUsernamesString, _ := datastore.GetString(blockedUsernamesKey)
if forbiddenUsernamesString != "" {
@@ -59,28 +58,28 @@ func migrateToDatastoreValues1(datastore *data.Datastore) {
}
}
func migrateToDatastoreValues2(datastore *data.Datastore, configRepository ConfigRepository) {
func migrateToDatastoreValues2(datastore *Datastore) {
oldAdminPassword, _ := datastore.GetString("stream_key")
// Avoids double hashing the password
_ = datastore.SetString("admin_password_key", oldAdminPassword)
_ = configRepository.SetStreamKeys([]models.StreamKey{
_ = SetStreamKeys([]models.StreamKey{
{Key: oldAdminPassword, Comment: "Default stream key"},
})
}
func migrateToDatastoreValues3ServingEndpoint3(configRepository ConfigRepository) {
s3Config := configRepository.GetS3Config()
func migrateToDatastoreValues3ServingEndpoint3(_ *Datastore) {
s3Config := GetS3Config()
if !s3Config.Enabled {
return
}
_ = configRepository.SetVideoServingEndpoint(s3Config.ServingEndpoint)
_ = SetVideoServingEndpoint(s3Config.ServingEndpoint)
}
func migrateToDatastoreValues4(datastore *data.Datastore, configRepository ConfigRepository) {
func migrateToDatastoreValues4(datastore *Datastore) {
unhashed_pass, _ := datastore.GetString("admin_password_key")
err := configRepository.SetAdminPassword(unhashed_pass)
err := SetAdminPassword(unhashed_pass)
if err != nil {
log.Fatalln("error migrating admin password:", err)
}

54
core/data/defaults.go Normal file
View File

@@ -0,0 +1,54 @@
package data
import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/models"
)
// HasPopulatedDefaults will determine if the defaults have been inserted into the database.
func HasPopulatedDefaults() bool {
hasPopulated, err := _datastore.GetBool("HAS_POPULATED_DEFAULTS")
if err != nil {
return false
}
return hasPopulated
}
func hasPopulatedFederationDefaults() bool {
hasPopulated, err := _datastore.GetBool("HAS_POPULATED_FEDERATION_DEFAULTS")
if err != nil {
return false
}
return hasPopulated
}
// PopulateDefaults will set default values in the database.
func PopulateDefaults() {
_datastore.warmCache()
defaults := config.GetDefaults()
if HasPopulatedDefaults() {
return
}
_ = SetAdminPassword(defaults.AdminPassword)
_ = SetStreamKeys(defaults.StreamKeys)
_ = SetHTTPPortNumber(float64(defaults.WebServerPort))
_ = SetRTMPPortNumber(float64(defaults.RTMPServerPort))
_ = SetLogoPath(defaults.Logo)
_ = SetServerMetadataTags([]string{"owncast", "streaming"})
_ = SetServerSummary(defaults.Summary)
_ = SetServerWelcomeMessage("")
_ = SetServerName(defaults.Name)
_ = SetExtraPageBodyContent(defaults.PageBodyContent)
_ = SetFederationGoLiveMessage(defaults.FederationGoLiveMessage)
_ = SetSocialHandles([]models.SocialHandle{
{
Platform: "github",
URL: "https://github.com/owncast/owncast",
},
})
_ = _datastore.SetBool("HAS_POPULATED_DEFAULTS", true)
}

View File

@@ -1,5 +1,14 @@
package data
import (
"context"
"database/sql"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus"
)
// GetMessagesCount will return the number of messages in the database.
func GetMessagesCount() int64 {
query := `SELECT COUNT(*) FROM messages`
@@ -16,3 +25,58 @@ func GetMessagesCount() int64 {
}
return count
}
// CreateBanIPTable will create the IP ban table if needed.
func CreateBanIPTable(db *sql.DB) {
createTableSQL := ` CREATE TABLE IF NOT EXISTS ip_bans (
"ip_address" TEXT NOT NULL PRIMARY KEY,
"notes" TEXT,
"created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);`
stmt, err := db.Prepare(createTableSQL)
if err != nil {
log.Fatal("error creating ip ban table", err)
}
defer stmt.Close()
if _, err := stmt.Exec(); err != nil {
log.Fatal("error creating ip ban table", err)
}
}
// BanIPAddress will persist a new IP address ban to the datastore.
func BanIPAddress(address, note string) error {
return _datastore.GetQueries().BanIPAddress(context.Background(), db.BanIPAddressParams{
IpAddress: address,
Notes: sql.NullString{String: note, Valid: true},
})
}
// IsIPAddressBanned will return if an IP address has been previously blocked.
func IsIPAddressBanned(address string) (bool, error) {
blocked, error := _datastore.GetQueries().IsIPAddressBlocked(context.Background(), address)
return blocked > 0, error
}
// GetIPAddressBans will return all the banned IP addresses.
func GetIPAddressBans() ([]models.IPAddress, error) {
result, err := _datastore.GetQueries().GetIPAddressBans(context.Background())
if err != nil {
return nil, err
}
response := []models.IPAddress{}
for _, ip := range result {
response = append(response, models.IPAddress{
IPAddress: ip.IpAddress,
Notes: ip.Notes.String,
CreatedAt: ip.CreatedAt.Time,
})
}
return response, err
}
// RemoveIPAddressBan will remove a previously banned IP address.
func RemoveIPAddressBan(address string) error {
return _datastore.GetQueries().RemoveIPAddressBan(context.Background(), address)
}

View File

@@ -5,11 +5,12 @@ import (
"database/sql"
"encoding/gob"
"sync"
"time"
// sqlite requires a blank import.
_ "github.com/mattn/go-sqlite3"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/db"
"github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus"
)
@@ -20,8 +21,7 @@ type Datastore struct {
DbLock *sync.Mutex
}
// WarmCache pre-caches all configuration values in memory.
func (ds *Datastore) WarmCache() {
func (ds *Datastore) warmCache() {
log.Traceln("Warming config value cache")
res, err := ds.DB.Query("SELECT key, value FROM datastore")
@@ -46,10 +46,10 @@ func (ds *Datastore) GetQueries() *db.Queries {
}
// Get will query the database for the key and return the entry.
func (ds *Datastore) Get(key string) (models.ConfigEntry, error) {
func (ds *Datastore) Get(key string) (ConfigEntry, error) {
cachedValue, err := ds.GetCachedValue(key)
if err == nil {
return models.ConfigEntry{
return ConfigEntry{
Key: key,
Value: cachedValue,
}, nil
@@ -60,10 +60,10 @@ func (ds *Datastore) Get(key string) (models.ConfigEntry, error) {
row := ds.DB.QueryRow("SELECT key, value FROM datastore WHERE key = ? LIMIT 1", key)
if err := row.Scan(&resultKey, &resultValue); err != nil {
return models.ConfigEntry{}, err
return ConfigEntry{}, err
}
result := models.ConfigEntry{
result := ConfigEntry{
Key: resultKey,
Value: resultValue,
}
@@ -73,7 +73,7 @@ func (ds *Datastore) Get(key string) (models.ConfigEntry, error) {
}
// Save will save the ConfigEntry to the database.
func (ds *Datastore) Save(e models.ConfigEntry) error {
func (ds *Datastore) Save(e ConfigEntry) error {
ds.DbLock.Lock()
defer ds.DbLock.Unlock()
@@ -93,6 +93,7 @@ func (ds *Datastore) Save(e models.ConfigEntry) error {
return err
}
_, err = stmt.Exec(e.Key, dataGob.Bytes())
if err != nil {
return err
}
@@ -120,6 +121,26 @@ func (ds *Datastore) Setup() {
);`
ds.MustExec(createTableSQL)
if !HasPopulatedDefaults() {
PopulateDefaults()
}
if !hasPopulatedFederationDefaults() {
if err := SetFederationGoLiveMessage(config.GetDefaults().FederationGoLiveMessage); err != nil {
log.Errorln(err)
}
if err := _datastore.SetBool("HAS_POPULATED_FEDERATION_DEFAULTS", true); err != nil {
log.Errorln(err)
}
}
// Set the server initialization date if needed.
if hasSetInitDate, _ := GetServerInitTime(); hasSetInitDate == nil || !hasSetInitDate.Valid {
_ = SetServerInitTime(time.Now())
}
migrateDatastoreValues(_datastore)
}
// Reset will delete all config entries in the datastore and start over.
@@ -135,6 +156,8 @@ func (ds *Datastore) Reset() {
if _, err = stmt.Exec(); err != nil {
log.Fatalln(err)
}
PopulateDefaults()
}
// GetDatastore returns the shared instance of the owncast datastore.

View File

@@ -1,19 +1,17 @@
package data
import "github.com/owncast/owncast/models"
// GetStringSlice will return the string slice value for a key.
func (ds *Datastore) GetStringSlice(key string) ([]string, error) {
configEntry, err := ds.Get(key)
if err != nil {
return []string{}, err
}
return configEntry.GetStringSlice()
return configEntry.getStringSlice()
}
// SetStringSlice will set the string slice value for a key.
func (ds *Datastore) SetStringSlice(key string, value []string) error {
configEntry := models.ConfigEntry{Value: value, Key: key}
configEntry := ConfigEntry{value, key}
return ds.Save(configEntry)
}
@@ -23,12 +21,12 @@ func (ds *Datastore) GetString(key string) (string, error) {
if err != nil {
return "", err
}
return configEntry.GetString()
return configEntry.getString()
}
// SetString will set the string value for a key.
func (ds *Datastore) SetString(key string, value string) error {
configEntry := models.ConfigEntry{Value: value, Key: key}
configEntry := ConfigEntry{value, key}
return ds.Save(configEntry)
}
@@ -38,12 +36,12 @@ func (ds *Datastore) GetNumber(key string) (float64, error) {
if err != nil {
return 0, err
}
return configEntry.GetNumber()
return configEntry.getNumber()
}
// SetNumber will set the numeric value for a key.
func (ds *Datastore) SetNumber(key string, value float64) error {
configEntry := models.ConfigEntry{Value: value, Key: key}
configEntry := ConfigEntry{value, key}
return ds.Save(configEntry)
}
@@ -53,12 +51,12 @@ func (ds *Datastore) GetBool(key string) (bool, error) {
if err != nil {
return false, err
}
return configEntry.GetBool()
return configEntry.getBool()
}
// SetBool will set the boolean value for a key.
func (ds *Datastore) SetBool(key string, value bool) error {
configEntry := models.ConfigEntry{Value: value, Key: key}
configEntry := ConfigEntry{value, key}
return ds.Save(configEntry)
}
@@ -68,11 +66,11 @@ func (ds *Datastore) GetStringMap(key string) (map[string]string, error) {
if err != nil {
return map[string]string{}, err
}
return configEntry.GetStringMap()
return configEntry.getStringMap()
}
// SetStringMap will set the string map value for a key.
func (ds *Datastore) SetStringMap(key string, value map[string]string) error {
configEntry := models.ConfigEntry{Value: value, Key: key}
configEntry := ConfigEntry{value, key}
return ds.Save(configEntry)
}

View File

@@ -12,8 +12,8 @@ import (
"github.com/nareix/joy5/format/rtmp"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
)
var _hasInboundRTMPConnection = false
@@ -33,9 +33,7 @@ func Start(setStreamAsConnected func(*io.PipeReader), setBroadcaster func(models
_setStreamAsConnected = setStreamAsConnected
_setBroadcaster = setBroadcaster
configRepository := configrepository.Get()
port := configRepository.GetRTMPPortNumber()
port := data.GetRTMPPortNumber()
s := rtmp.NewServer()
var lis net.Listener
var err error
@@ -80,10 +78,8 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) {
return
}
configRepository := configrepository.Get()
accessGranted := false
validStreamingKeys := configRepository.GetStreamKeys()
validStreamingKeys := data.GetStreamKeys()
// If a stream key override was specified then use that instead.
if config.TemporaryStreamKey != "" {

View File

@@ -7,8 +7,8 @@ import (
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/services/geoip"
)
@@ -48,8 +48,7 @@ func IsStreamConnected() bool {
// Kind of a hack. It takes a handful of seconds between a RTMP connection and when HLS data is available.
// So account for that with an artificial buffer of four segments.
timeSinceLastConnected := time.Since(_stats.LastConnectTime.Time).Seconds()
configRepository := configrepository.Get()
waitTime := math.Max(float64(configRepository.GetStreamLatencyLevel().SecondsPerSegment)*3.0, 7)
waitTime := math.Max(float64(data.GetStreamLatencyLevel().SecondsPerSegment)*3.0, 7)
if timeSinceLastConnected < waitTime {
return false
}
@@ -76,7 +75,7 @@ func SetViewerActive(viewer *models.Viewer) {
l.Lock()
defer l.Unlock()
// Asynchronously, optionally, fetch GeoIP configRepository.
// Asynchronously, optionally, fetch GeoIP data.
go func(viewer *models.Viewer) {
viewer.Geo = _geoIPClient.GetGeoFromIP(viewer.IPAddress)
}(viewer)
@@ -112,29 +111,27 @@ func pruneViewerCount() {
}
func saveStats() {
configRepository := configrepository.Get()
if err := configRepository.SetPeakOverallViewerCount(_stats.OverallMaxViewerCount); err != nil {
if err := data.SetPeakOverallViewerCount(_stats.OverallMaxViewerCount); err != nil {
log.Errorln("error saving viewer count", err)
}
if err := configRepository.SetPeakSessionViewerCount(_stats.SessionMaxViewerCount); err != nil {
if err := data.SetPeakSessionViewerCount(_stats.SessionMaxViewerCount); err != nil {
log.Errorln("error saving viewer count", err)
}
if _stats.LastDisconnectTime != nil && _stats.LastDisconnectTime.Valid {
if err := configRepository.SetLastDisconnectTime(_stats.LastDisconnectTime.Time); err != nil {
if err := data.SetLastDisconnectTime(_stats.LastDisconnectTime.Time); err != nil {
log.Errorln("error saving disconnect time", err)
}
}
}
func getSavedStats() models.Stats {
configRepository := configrepository.Get()
savedLastDisconnectTime, _ := configRepository.GetLastDisconnectTime()
savedLastDisconnectTime, _ := data.GetLastDisconnectTime()
result := models.Stats{
ChatClients: make(map[string]models.Client),
Viewers: make(map[string]*models.Viewer),
SessionMaxViewerCount: configRepository.GetPeakSessionViewerCount(),
OverallMaxViewerCount: configRepository.GetPeakOverallViewerCount(),
SessionMaxViewerCount: data.GetPeakSessionViewerCount(),
OverallMaxViewerCount: data.GetPeakOverallViewerCount(),
LastDisconnectTime: savedLastDisconnectTime,
}

View File

@@ -2,8 +2,8 @@ package core
import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
)
// GetStatus gets the status of the system.
@@ -17,7 +17,6 @@ func GetStatus() models.Status {
viewerCount = len(_stats.Viewers)
}
configRepository := configrepository.Get()
return models.Status{
Online: IsStreamConnected(),
ViewerCount: viewerCount,
@@ -26,7 +25,7 @@ func GetStatus() models.Status {
LastDisconnectTime: _stats.LastDisconnectTime,
LastConnectTime: _stats.LastConnectTime,
VersionNumber: config.VersionNumber,
StreamTitle: configRepository.GetStreamTitle(),
StreamTitle: data.GetStreamTitle(),
}
}

View File

@@ -1,13 +1,12 @@
package core
import (
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/storageproviders"
"github.com/owncast/owncast/persistence/configrepository"
)
func setupStorage() error {
configRepository := configrepository.Get()
s3Config := configRepository.GetS3Config()
s3Config := data.GetS3Config()
if s3Config.Enabled {
_storage = storageproviders.NewS3Storage()

View File

@@ -5,8 +5,9 @@ import (
"path/filepath"
"sort"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/core/data"
)
// LocalStorage represents an instance of the local storage provider for HLS video.
@@ -21,8 +22,7 @@ func NewLocalStorage() *LocalStorage {
// Setup configures this storage provider.
func (s *LocalStorage) Setup() error {
configRepository := configrepository.Get()
s.host = configRepository.GetVideoServingEndpoint()
s.host = data.GetVideoServingEndpoint()
return nil
}
@@ -63,8 +63,7 @@ func (s *LocalStorage) Save(filePath string, retryCount int) (string, error) {
// Cleanup will remove old files from the storage provider.
func (s *LocalStorage) Cleanup() error {
// Determine how many files we should keep on disk
configRepository := configrepository.Get()
maxNumber := configRepository.GetStreamLatencyLevel().SegmentCount
maxNumber := data.GetStreamLatencyLevel().SegmentCount
buffer := 10
return localCleanup(maxNumber + buffer)
}

View File

@@ -11,7 +11,7 @@ import (
"sync"
"time"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/utils"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
@@ -64,9 +64,9 @@ func NewS3Storage() *S3Storage {
// Setup sets up the s3 storage for saving the video to s3.
func (s *S3Storage) Setup() error {
log.Trace("Setting up S3 for external storage of video...")
configRepository := configrepository.Get()
s3Config := configRepository.GetS3Config()
customVideoServingEndpoint := configRepository.GetVideoServingEndpoint()
s3Config := data.GetS3Config()
customVideoServingEndpoint := data.GetVideoServingEndpoint()
if customVideoServingEndpoint != "" {
s.host = customVideoServingEndpoint
@@ -106,9 +106,8 @@ func (s *S3Storage) SegmentWritten(localFilePath string) {
averagePerformance := utils.GetAveragePerformance(performanceMonitorKey)
// Warn the user about long-running save operations
configRepository := configrepository.Get()
if averagePerformance != 0 {
if averagePerformance > float64(configRepository.GetStreamLatencyLevel().SecondsPerSegment)*0.9 {
if averagePerformance > float64(data.GetStreamLatencyLevel().SecondsPerSegment)*0.9 {
log.Warnln("Possible slow uploads: average upload S3 save duration", averagePerformance, "s. troubleshoot this issue by visiting https://owncast.online/docs/troubleshooting/")
}
}
@@ -221,8 +220,7 @@ func (s *S3Storage) Cleanup() error {
// RemoteCleanup will remove old files from the remote storage provider.
func (s *S3Storage) RemoteCleanup() error {
// Determine how many files we should keep on S3 storage
configRepository := configrepository.Get()
maxNumber := configRepository.GetStreamLatencyLevel().SegmentCount
maxNumber := data.GetStreamLatencyLevel().SegmentCount
buffer := 20
keys, err := s.getDeletableVideoSegmentsWithOffset(maxNumber + buffer)

View File

@@ -16,7 +16,6 @@ import (
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/notifications"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
)
@@ -40,11 +39,9 @@ func setStreamAsConnected(rtmpOut *io.PipeReader) {
_stats.LastConnectTime = &now
_stats.SessionMaxViewerCount = 0
configRepository := configrepository.Get()
_currentBroadcast = &models.CurrentBroadcast{
LatencyLevel: configRepository.GetStreamLatencyLevel(),
OutputSettings: configRepository.GetStreamOutputVariants(),
LatencyLevel: data.GetStreamLatencyLevel(),
OutputSettings: data.GetStreamOutputVariants(),
}
StopOfflineCleanupTimer()
@@ -72,7 +69,7 @@ func setStreamAsConnected(rtmpOut *io.PipeReader) {
}()
go webhooks.SendStreamStatusEvent(models.StreamStarted)
selectedThumbnailVideoQualityIndex, isVideoPassthrough := configRepository.FindHighestVideoQualityIndex(_currentBroadcast.OutputSettings)
selectedThumbnailVideoQualityIndex, isVideoPassthrough := data.FindHighestVideoQualityIndex(_currentBroadcast.OutputSettings)
transcoder.StartThumbnailGenerator(segmentPath, selectedThumbnailVideoQualityIndex, isVideoPassthrough)
_ = chat.SendSystemAction("Stay tuned, the stream is **starting**!", true)
@@ -179,9 +176,8 @@ func startLiveStreamNotificationsTimer() context.CancelFunc {
return
}
configRepository := configrepository.Get()
// Send Fediverse message.
if configRepository.GetFederationEnabled() {
if data.GetFederationEnabled() {
log.Traceln("Sending Federated Go Live message.")
if err := activitypub.SendLive(); err != nil {
log.Errorln(err)

View File

@@ -27,7 +27,6 @@ var supportedCodecs = map[string]string{
(&Libx264Codec{}).Name(): "libx264",
(&OmxCodec{}).Name(): "omx",
(&VaapiCodec{}).Name(): "vaapi",
(&QuicksyncCodec{}).Name(): "qsv",
(&NvencCodec{}).Name(): "NVIDIA nvenc",
(&VideoToolboxCodec{}).Name(): "videotoolbox",
}
@@ -192,7 +191,7 @@ func (c *VaapiCodec) GlobalFlags() string {
// PixelFormat is the pixel format required for this codec.
func (c *VaapiCodec) PixelFormat() string {
return "vaapi"
return "vaapi_vld"
}
// Scaler is the scaler used for resizing the video in the transcoder.
@@ -202,7 +201,7 @@ func (c *VaapiCodec) Scaler() string {
// ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *VaapiCodec) ExtraFilters() string {
return "hwupload=extra_hw_frames=64,format=vaapi"
return ""
}
// ExtraArguments are the extra arguments used with this codec in the transcoder.
@@ -318,22 +317,17 @@ func (c *QuicksyncCodec) DisplayName() string {
// GlobalFlags are the global flags used with this codec in the transcoder.
func (c *QuicksyncCodec) GlobalFlags() string {
flags := []string{
"-init_hw_device", "qsv=hw",
"-filter_hw_device", "hw",
}
return strings.Join(flags, " ")
return ""
}
// PixelFormat is the pixel format required for this codec.
func (c *QuicksyncCodec) PixelFormat() string {
return "qsv"
return "nv12"
}
// Scaler is the scaler used for resizing the video in the transcoder.
func (c *QuicksyncCodec) Scaler() string {
return "scale_qsv"
return ""
}
// ExtraArguments are the extra arguments used with this codec in the transcoder.
@@ -343,7 +337,7 @@ func (c *QuicksyncCodec) ExtraArguments() string {
// ExtraFilters are the extra filters required for this codec in the transcoder.
func (c *QuicksyncCodec) ExtraFilters() string {
return "hwupload=extra_hw_frames=64,format=qsv"
return ""
}
// VariantFlags returns a string representing a single variant processed by this codec.
@@ -354,16 +348,16 @@ func (c *QuicksyncCodec) VariantFlags(v *HLSVariant) string {
// GetPresetForLevel returns the string preset for this codec given an integer level.
func (c *QuicksyncCodec) GetPresetForLevel(l int) string {
presetMapping := map[int]string{
0: "veryfast",
1: "fast",
2: "medium",
3: "slow",
4: "veryslow",
0: "ultrafast",
1: "superfast",
2: "veryfast",
3: "faster",
4: "fast",
}
preset, ok := presetMapping[l]
if !ok {
defaultPreset := presetMapping[2]
defaultPreset := presetMapping[1]
log.Errorf("Invalid level for quicksync preset %d, defaulting to %s", l, defaultPreset)
return defaultPreset
}

View File

@@ -11,7 +11,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/utils"
)
@@ -88,9 +88,9 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
if len(names) == 0 {
return nil
}
configRepository := configrepository.Get()
mostRecentFile := path.Join(framePath, names[0])
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath())
outputFileTemp := path.Join(config.TempDir, "tempthumbnail.jpg")
thumbnailCmdFlags := []string{
@@ -120,8 +120,7 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
}
func makeAnimatedGifPreview(sourceFile string, outputFile string) {
configRepository := configrepository.Get()
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath())
outputFileTemp := path.Join(config.TempDir, "temppreview.gif")
// Filter is pulled from https://engineering.giphy.com/how-to-make-gifs-with-ffmpeg/

View File

@@ -12,9 +12,9 @@ import (
"github.com/teris-io/shortid"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/logging"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
)
@@ -229,8 +229,7 @@ func (t *Transcoder) getString() string {
"-hls_segment_filename", localListenerAddress + "/%v/stream-" + t.segmentIdentifier + "-%d.ts", // Send HLS segments back to us over HTTP
"-max_muxing_queue_size", "400", // Workaround for Too many packets error: https://trac.ffmpeg.org/ticket/6375?cversion=0
"-method PUT", // HLS results sent back to us will be over PUTs
"-method PUT", // HLS results sent back to us will be over PUTs
localListenerAddress + "/%v/stream.m3u8", // Send HLS playlists back to us over HTTP
}
@@ -274,16 +273,15 @@ func getVariantFromConfigQuality(quality models.StreamOutputVariant, index int)
// NewTranscoder will return a new Transcoder, populated by the config.
func NewTranscoder() *Transcoder {
configRepository := configrepository.Get()
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath())
transcoder := new(Transcoder)
transcoder.ffmpegPath = ffmpegPath
transcoder.internalListenerPort = config.InternalHLSListenerPort
transcoder.currentStreamOutputSettings = configRepository.GetStreamOutputVariants()
transcoder.currentLatencyLevel = configRepository.GetStreamLatencyLevel()
transcoder.codec = getCodec(configRepository.GetVideoCodec())
transcoder.currentStreamOutputSettings = data.GetStreamOutputVariants()
transcoder.currentLatencyLevel = data.GetStreamLatencyLevel()
transcoder.codec = getCodec(data.GetVideoCodec())
transcoder.segmentOutputPath = config.HLSStoragePath
transcoder.playlistOutputPath = config.HLSStoragePath

View File

@@ -1,50 +0,0 @@
package transcoder
import (
"path/filepath"
"testing"
"github.com/owncast/owncast/models"
)
func TestFFmpegQuicksyncCommand(t *testing.T) {
latencyLevel := models.GetLatencyLevel(2)
codec := QuicksyncCodec{}
transcoder := new(Transcoder)
transcoder.ffmpegPath = filepath.Join("fake", "path", "ffmpeg")
transcoder.SetInput("fakecontent.flv")
transcoder.SetOutputPath("fakeOutput")
transcoder.SetIdentifier("jdofFGg")
transcoder.SetInternalHTTPPort("8123")
transcoder.SetCodec(codec.Name())
transcoder.currentLatencyLevel = latencyLevel
variant := HLSVariant{}
variant.videoBitrate = 1200
variant.isAudioPassthrough = true
variant.SetVideoFramerate(30)
variant.SetCPUUsageLevel(2)
transcoder.AddVariant(variant)
variant2 := HLSVariant{}
variant2.videoBitrate = 3500
variant2.isAudioPassthrough = true
variant2.SetVideoFramerate(24)
variant2.SetCPUUsageLevel(4)
transcoder.AddVariant(variant2)
variant3 := HLSVariant{}
variant3.isAudioPassthrough = true
variant3.isVideoPassthrough = true
transcoder.AddVariant(variant3)
cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -init_hw_device qsv=hw -filter_hw_device hw -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_qsv -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -filter:v:0 "hwupload=extra_hw_frames=64,format=qsv" -preset medium -map v:0 -c:v:1 h264_qsv -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -filter:v:1 "hwupload=extra_hw_frames=64,format=qsv" -preset veryslow -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset veryfast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt qsv -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
}
}

View File

@@ -42,7 +42,7 @@ func TestFFmpegVaapiCommand(t *testing.T) {
cmd := transcoder.getString()
expectedLogPath := filepath.Join("data", "logs", "transcoder.log")
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device /dev/dri/renderD128 -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_vaapi -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -filter:v:0 "hwupload=extra_hw_frames=64,format=vaapi" -preset veryfast -map v:0 -c:v:1 h264_vaapi -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -filter:v:1 "hwupload=extra_hw_frames=64,format=vaapi" -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt vaapi -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
expected := `FFREPORT=file="` + expectedLogPath + `":level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device /dev/dri/renderD128 -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_vaapi -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_vaapi -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -map a:0? -c:a:1 copy -preset fast -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset ultrafast -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2 " -f hls -hls_time 3 -hls_list_size 10 -hls_flags program_date_time+independent_segments+omit_endlist -segment_format_options mpegts_flags=mpegts_copyts=1 -pix_fmt vaapi_vld -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
if cmd != expected {
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)

View File

@@ -8,7 +8,7 @@ import (
"sync"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
@@ -59,8 +59,6 @@ var ignoredErrors = []string{
"Non-monotonous DTS in output",
"frames duplicated",
"To ignore this",
"Driver does not support some wanted packed headers (wanted 0xd, found 0x1)",
"Failed to allocate a vaapi/nv12 frame from a fixed pool of hardware frames.",
}
func handleTranscoderMessage(message string) {
@@ -101,9 +99,9 @@ func handleTranscoderMessage(message string) {
func createVariantDirectories() {
// Create private hls data dirs
utils.CleanupDirectory(config.HLSStoragePath)
configRepository := configrepository.Get()
if len(configRepository.GetStreamOutputVariants()) != 0 {
for index := range configRepository.GetStreamOutputVariants() {
if len(data.GetStreamOutputVariants()) != 0 {
for index := range data.GetStreamOutputVariants() {
if err := os.MkdirAll(path.Join(config.HLSStoragePath, strconv.Itoa(index)), 0o750); err != nil {
log.Fatalln(err)
}

View File

@@ -3,8 +3,8 @@ package webhooks
import (
"time"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/teris-io/shortid"
)
@@ -14,15 +14,13 @@ func SendStreamStatusEvent(eventType models.EventType) {
}
func sendStreamStatusEvent(eventType models.EventType, id string, timestamp time.Time) {
configRepository := configrepository.Get()
SendEventToWebhooks(WebhookEvent{
Type: eventType,
EventData: map[string]interface{}{
"id": id,
"name": configRepository.GetServerName(),
"summary": configRepository.GetServerSummary(),
"streamTitle": configRepository.GetStreamTitle(),
"name": data.GetServerName(),
"summary": data.GetServerSummary(),
"streamTitle": data.GetStreamTitle(),
"status": getStatus(),
"timestamp": timestamp,
},

View File

@@ -5,16 +5,14 @@ import (
"time"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
)
func TestSendStreamStatusEvent(t *testing.T) {
configRepository := configrepository.Get()
configRepository.SetServerName("my server")
configRepository.SetServerSummary("my server where I stream")
configRepository.SetStreamTitle("my stream")
data.SetServerName("my server")
data.SetServerSummary("my server where I stream")
data.SetStreamTitle("my stream")
checkPayload(t, models.StreamStarted, func() {
sendStreamStatusEvent(events.StreamStarted, "id", time.Unix(72, 6).UTC())

View File

@@ -1,11 +0,0 @@
project_id_env: CROWDIN_PROJECT_ID
api_token_env: CROWDIN_PERSONAL_TOKEN
pull_request_title: Translations update
pull_request_labels:
- crowdin
- i18n
- translation
commit_message: Updated translations
files:
- source: /web/i18n/en/translation.json
translation: /web/i18n/%two_letters_code%/translation.json

View File

@@ -158,7 +158,7 @@ UPDATE users SET display_color = $1 WHERE id = $2
`
type ChangeDisplayColorParams struct {
DisplayColor int
DisplayColor int32
ID string
}
@@ -253,8 +253,8 @@ SELECT iri, inbox, name, username, image, created_at FROM ap_followers WHERE app
`
type GetFederationFollowersWithOffsetParams struct {
Limit int
Offset int
Limit int32
Offset int32
}
type GetFederationFollowersWithOffsetRow struct {
@@ -365,8 +365,8 @@ SELECT iri, actor, type, timestamp FROM ap_accepted_activities ORDER BY timestam
`
type GetInboundActivitiesWithOffsetParams struct {
Limit int
Offset int
Limit int32
Offset int32
}
type GetInboundActivitiesWithOffsetRow struct {
@@ -514,8 +514,8 @@ SELECT value FROM ap_outbox LIMIT $1 OFFSET $2
`
type GetOutboxWithOffsetParams struct {
Limit int
Offset int
Limit int32
Offset int32
}
func (q *Queries) GetOutboxWithOffset(ctx context.Context, arg GetOutboxWithOffsetParams) ([][]byte, error) {

File diff suppressed because one or more lines are too long

111
go.mod
View File

@@ -1,81 +1,94 @@
module github.com/owncast/owncast
go 1.22.0
toolchain go1.23.1
go 1.22
require (
github.com/CAFxX/httpcompression v0.0.9
github.com/SherClockHolmes/webpush-go v1.4.0
github.com/TwiN/go-away v1.6.14
github.com/andybalholm/cascadia v1.3.3
github.com/aws/aws-sdk-go v1.55.6
github.com/go-chi/chi/v5 v5.2.0
github.com/go-fed/activity v1.0.1-0.20220119073622-b14b50eecad0
github.com/aws/aws-sdk-go v1.54.13
github.com/go-fed/activity v1.0.1-0.20210803212804-d866ba75dd0f
github.com/go-fed/httpsig v1.1.0
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/gorilla/websocket v1.5.3
github.com/grafov/m3u8 v0.12.1
github.com/jellydator/ttlcache/v3 v3.3.0
github.com/grafov/m3u8 v0.12.0
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
github.com/mattn/go-sqlite3 v1.14.24
github.com/mattn/go-sqlite3 v1.14.22
github.com/microcosm-cc/bluemonday v1.0.27
github.com/mssola/user_agent v0.6.0
github.com/nakabonne/tstorage v0.3.6
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590
github.com/oapi-codegen/runtime v1.1.1
github.com/oschwald/geoip2-golang v1.11.0
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.20.5
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/schollz/sqlite3dump v1.3.1
github.com/shirou/gopsutil/v4 v4.24.12
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569
github.com/yuin/goldmark v1.7.8
github.com/yuin/goldmark-emoji v1.0.4
golang.org/x/crypto v0.32.0
golang.org/x/mod v0.22.0
golang.org/x/net v0.34.0
golang.org/x/time v0.9.0
gopkg.in/evanphx/json-patch.v5 v5.9.0
mvdan.cc/xurls/v2 v2.6.0
github.com/yuin/goldmark v1.7.4
golang.org/x/mod v0.18.0
golang.org/x/time v0.5.0
)
require (
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/lestrrat-go/strftime v1.0.4 // indirect
github.com/mvdan/xurls v1.1.0 // indirect
github.com/pkg/errors v0.9.1
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/crypto v0.24.0
golang.org/x/net v0.26.0
golang.org/x/sys v0.21.0 // indirect
)
require github.com/prometheus/client_golang v1.19.1
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)
require (
github.com/nakabonne/tstorage v0.3.6
github.com/shirou/gopsutil/v4 v4.24.6
)
require github.com/SherClockHolmes/webpush-go v1.3.0
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/ebitengine/purego v0.8.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-test/deep v1.0.4 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/lestrrat-go/strftime v1.0.4 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oschwald/maxminddb-golang v1.13.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
golang.org/x/sync v0.7.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
github.com/CAFxX/httpcompression v0.0.9
github.com/TwiN/go-away v1.6.13
github.com/andybalholm/cascadia v1.3.2
github.com/go-chi/chi/v5 v5.1.0
github.com/jellydator/ttlcache/v3 v3.2.0
github.com/mssola/user_agent v0.6.0
github.com/oapi-codegen/runtime v1.1.1
github.com/shirou/gopsutil/v3 v3.24.5
github.com/stretchr/testify v1.9.0
github.com/yuin/goldmark-emoji v1.0.3
gopkg.in/evanphx/json-patch.v5 v5.9.0
mvdan.cc/xurls v1.1.0
mvdan.cc/xurls/v2 v2.5.0
)
replace github.com/go-fed/activity => github.com/owncast/activity v1.0.1-0.20211229051252-7821289d4026

193
go.sum
View File

@@ -3,41 +3,33 @@ github.com/CAFxX/httpcompression v0.0.9/go.mod h1:XX8oPZA+4IDcfZ0A71Hz0mZsv/YJOg
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/SherClockHolmes/webpush-go v1.3.0 h1:CAu3FvEE9QS4drc3iKNgpBWFfGqNthKlZhp5QpYnu6k=
github.com/SherClockHolmes/webpush-go v1.3.0/go.mod h1:AxRHmJuYwKGG1PVgYzToik1lphQvDnqFYDqimHvwhIw=
github.com/SherClockHolmes/webpush-go v1.4.0 h1:ocnzNKWN23T9nvHi6IfyrQjkIc0oJWv1B1pULsf9i3s=
github.com/SherClockHolmes/webpush-go v1.4.0/go.mod h1:XSq8pKX11vNV8MJEMwjrlTkxhAj1zKfxmyhdV7Pd6UA=
github.com/TwiN/go-away v1.6.13 h1:aB6l/FPXmA5ds+V7I9zdhxzpsLLUvVtEuS++iU/ZmgE=
github.com/TwiN/go-away v1.6.13/go.mod h1:MpvIC9Li3minq+CGgbgUDvQ9tDaeW35k5IXZrF9MVas=
github.com/TwiN/go-away v1.6.14 h1:gjFP+6/A36gmj0NpYX0Sz9hrdU0KtHwtNWYnsJgV4fo=
github.com/TwiN/go-away v1.6.14/go.mod h1:d+Gv3XuqjIeFqXYuAIzlyNoDzr1vNsP5B/hRY3u/VLs=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go v1.53.5 h1:1OcVWMjGlwt7EU5OWmmEEXqaYfmX581EK317QJZXItM=
github.com/aws/aws-sdk-go v1.53.5/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go v1.54.13 h1:zpCuiG+/mFdDY/klKJvmSioAZWk45F4rLGq0JWVAAzk=
github.com/aws/aws-sdk-go v1.54.13/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/dave/jennifer v1.3.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-fed/httpsig v0.1.1-0.20190914113940-c2de3672e5b5/go.mod h1:T56HUNYZUQ1AGUzhAYPugZfp36sKApVnGBgKlIY+aIE=
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
@@ -48,26 +40,27 @@ github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f h1:jopqB+UTSdJGEJT8tEqYyE29zN91fi2827oLET8tl7k=
github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafov/m3u8 v0.12.0 h1:T6iTwTsSEtMcwkayef+FJO8kj+Sglr4Lh81Zj8Ked/4=
github.com/grafov/m3u8 v0.12.0/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080=
github.com/grafov/m3u8 v0.12.1 h1:DuP1uA1kvRRmGNAZ0m+ObLv1dvrfNO0TPx0c/enNk0s=
github.com/grafov/m3u8 v0.12.1/go.mod h1:nqzOkfBiZJENr52zTVd/Dcl03yzphIMbJqkXGu+u080=
github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc=
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE=
github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -75,16 +68,13 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
@@ -94,22 +84,28 @@ github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR7
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4=
github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mvdan/xurls v1.1.0 h1:OpuDelGQ1R1ueQ6sSryzi6P+1RtBpfQHM8fJwlE45ww=
github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU=
github.com/nakabonne/tstorage v0.3.6 h1:usp7pTohax8mynnFiUSUQ2QVBCKLCkYx3gmb3+rJo54=
github.com/nakabonne/tstorage v0.3.6/go.mod h1:1xUrK3s1MXSlU6dn96xHerHx/MdO4BGmsAHEUbsaOxU=
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590 h1:PnxRU8L8Y2q82vFC2QdNw23Dm2u6WrjecIdpXjiYbXM=
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590/go.mod h1:XmAOs6UJXpNXRwKk+KY/nv5kL6xXYXyellk+A1pTlko=
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/oschwald/geoip2-golang v1.9.0 h1:uvD3O6fXAXs+usU+UGExshpdP13GAqp4GBrzN7IgKZc=
github.com/oschwald/geoip2-golang v1.9.0/go.mod h1:BHK6TvDyATVQhKNbQBdrj9eAvuwOMi2zSFXizL3K81Y=
github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0=
github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg=
github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o=
github.com/owncast/activity v1.0.1-0.20211229051252-7821289d4026 h1:E1nxiX44BcMQTSSs8MHLm2rXnqXNedYZkFI31gXMsJc=
@@ -123,26 +119,29 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/schollz/sqlite3dump v1.3.1 h1:QXizJ7XEJ7hggjqjZ3YRtF3+javm8zKtzNByYtEkPRA=
github.com/schollz/sqlite3dump v1.3.1/go.mod h1:mzSTjZpJH4zAb1FN3iNlhWPbbdyeBpOaTW0hukyMHyI=
github.com/shirou/gopsutil/v4 v4.24.10 h1:7VOzPtfw/5YDU+jLEoBwXwxJbQetULywoSV4RYY7HkM=
github.com/shirou/gopsutil/v4 v4.24.10/go.mod h1:s4D/wg+ag4rG0WO7AiTj2BeYCRhym0vM7DHbZRxnIT8=
github.com/shirou/gopsutil/v4 v4.24.11 h1:WaU9xqGFKvFfsUv94SXcUPD7rCkU0vr/asVdQOBZNj8=
github.com/shirou/gopsutil/v4 v4.24.11/go.mod h1:s4D/wg+ag4rG0WO7AiTj2BeYCRhym0vM7DHbZRxnIT8=
github.com/shirou/gopsutil/v4 v4.24.12 h1:qvePBOk20e0IKA1QXrIIU+jmk+zEiYVVx06WjBRlZo4=
github.com/shirou/gopsutil/v4 v4.24.12/go.mod h1:DCtMPAad2XceTeIAbGyVfycbYQNBGk2P8cvDi7/VN9o=
github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shirou/gopsutil/v4 v4.24.6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
@@ -151,6 +150,7 @@ github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKk
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -159,8 +159,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 h1:xzABM9let0HLLqFypcxvLmlvEciCHL7+Lv+4vwZqecI=
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569/go.mod h1:2Ly+NIftZN4de9zRmENdYbvPQeaVIYKWpLFStLFEBgI=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
@@ -170,39 +168,35 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g=
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-emoji v1.0.4 h1:vCwMkPZSNefSUnOW2ZKRUjBSD5Ok3W78IXhGxxAEF90=
github.com/yuin/goldmark-emoji v1.0.4/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s=
github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4=
github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
golang.org/x/crypto v0.0.0-20180527072434-ab813273cd59/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -211,27 +205,17 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -246,55 +230,37 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -306,7 +272,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
mvdan.cc/xurls v1.1.0 h1:kj0j2lonKseISJCiq1Tfk+iTv65dDGCl0rTbanXJGGc=
mvdan.cc/xurls v1.1.0/go.mod h1:TNWuhvo+IqbUCmtUIb/3LJSQdrzel8loVpgFm0HikbI=
mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk=

15
main.go
View File

@@ -6,7 +6,6 @@ import (
"strconv"
"github.com/owncast/owncast/logging"
"github.com/owncast/owncast/persistence/configrepository"
log "github.com/sirupsen/logrus"
"github.com/owncast/owncast/config"
@@ -112,10 +111,8 @@ func main() {
}
func handleCommandLineFlags() {
configRepository := configrepository.Get()
if *newAdminPassword != "" {
if err := configRepository.SetAdminPassword(*newAdminPassword); err != nil {
if err := data.SetAdminPassword(*newAdminPassword); err != nil {
log.Errorln("Error setting your admin password.", err)
log.Exit(1)
} else {
@@ -137,25 +134,25 @@ func handleCommandLineFlags() {
}
log.Println("Saving new web server port number to", portNumber)
if err := configRepository.SetHTTPPortNumber(float64(portNumber)); err != nil {
if err := data.SetHTTPPortNumber(float64(portNumber)); err != nil {
log.Errorln(err)
}
}
config.WebServerPort = configRepository.GetHTTPPortNumber()
config.WebServerPort = data.GetHTTPPortNumber()
// Set the web server ip
if *webServerIPOverride != "" {
log.Println("Saving new web server listen IP address to", *webServerIPOverride)
if err := configRepository.SetHTTPListenAddress(*webServerIPOverride); err != nil {
if err := data.SetHTTPListenAddress(*webServerIPOverride); err != nil {
log.Errorln(err)
}
}
config.WebServerIP = configRepository.GetHTTPListenAddress()
config.WebServerIP = data.GetHTTPListenAddress()
// Set the rtmp server port
if *rtmpPortOverride > 0 {
log.Println("Saving new RTMP server port number to", *rtmpPortOverride)
if err := configRepository.SetRTMPPortNumber(float64(*rtmpPortOverride)); err != nil {
if err := data.SetRTMPPortNumber(float64(*rtmpPortOverride)); err != nil {
log.Errorln(err)
}
}

View File

@@ -3,9 +3,9 @@ package metrics
import (
"time"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/mem"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/mem"
log "github.com/sirupsen/logrus"
)

View File

@@ -5,8 +5,8 @@ import (
"sort"
"github.com/owncast/owncast/core"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/owncast/owncast/utils"
)
@@ -68,8 +68,8 @@ func networkSpeedHealthOverviewMessage() string {
isVideoPassthrough bool
bitrate int
}
configRepository := configrepository.Get()
outputVariants := configRepository.GetStreamOutputVariants()
outputVariants := data.GetStreamOutputVariants()
streamSortVariants := make([]singleVariant, len(outputVariants))
for i, variant := range outputVariants {
@@ -155,8 +155,7 @@ func wastefulBitrateOverviewMessage() string {
return ""
}
configRepository := configrepository.Get()
outputVariants := configRepository.GetStreamOutputVariants()
outputVariants := data.GetStreamOutputVariants()
type singleVariant struct {
isVideoPassthrough bool
@@ -230,8 +229,7 @@ func errorCountHealthOverviewMessage() string {
healthyPercentage := utils.IntPercentage(clientsWithErrors, totalNumberOfClients)
isUsingPassthrough := false
configRepository := configrepository.Get()
outputVariants := configRepository.GetStreamOutputVariants()
outputVariants := data.GetStreamOutputVariants()
for _, variant := range outputVariants {
if variant.IsVideoPassthrough {
isUsingPassthrough = true

View File

@@ -5,8 +5,8 @@ import (
"time"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/configrepository"
)
// How often we poll for updates.
@@ -56,9 +56,8 @@ var _getStatus func() models.Status
// Start will begin the metrics collection and alerting.
func Start(getStatus func() models.Status) {
configRepository := configrepository.Get()
_getStatus = getStatus
host := configRepository.GetServerURL()
host := data.GetServerURL()
if host == "" {
host = "unknown"
}

View File

@@ -31,7 +31,6 @@ func startViewerCollectionMetrics() {
func collectViewerCount() {
// Don't collect metrics for viewers if there's no stream active.
if !core.GetStatus().Online {
activeViewerCount.Set(0)
return
}

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