Compare commits
55
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e699bef636 | ||
|
|
854674b5fb | ||
|
|
873d7e0a5f | ||
|
|
519d239a87 | ||
|
|
c13eaf25dd | ||
|
|
324bf75f69 | ||
|
|
2c64efe505 | ||
|
|
722226e054 | ||
|
|
d10fc7f5d4 | ||
|
|
601553c582 | ||
|
|
8b5fe84e62 | ||
|
|
33efafecd5 | ||
|
|
8ccec822b4 | ||
|
|
e738156fd7 | ||
|
|
a2981903c2 | ||
|
|
5d4fcf9686 | ||
|
|
50854f09b3 | ||
|
|
54104a5c3c | ||
|
|
715c267079 | ||
|
|
c48cc989f4 | ||
|
|
44970293f8 | ||
|
|
423ddb8e22 | ||
|
|
c593ed381d | ||
|
|
b6a39721dd | ||
|
|
159e24438d | ||
|
|
2c3e8fa3c1 | ||
|
|
ba8ce8bf66 | ||
|
|
84341875b7 | ||
|
|
d52911160d | ||
|
|
4ef036b0f4 | ||
|
|
eb3184bcfa | ||
|
|
5c9b2333f9 | ||
|
|
77c7c08587 | ||
|
|
d9a424ea79 | ||
|
|
826c7e8a9e | ||
|
|
3e6f6de032 | ||
|
|
3909c506a2 | ||
|
|
e9556d44da | ||
|
|
c85529370b | ||
|
|
285a722fac | ||
|
|
234c696af9 | ||
|
|
177840b94a | ||
|
|
35e684bd10 | ||
|
|
9b4adab8fc | ||
|
|
d74d1b55f0 | ||
|
|
4d52d7d1fa | ||
|
|
02559acc39 | ||
|
|
892fd3d89e | ||
|
|
0bb372d8cb | ||
|
|
ddc8e9bed0 | ||
|
|
5b2a3e3127 | ||
|
|
a476142f23 | ||
|
|
ad090a0f86 | ||
|
|
5365cc0325 | ||
|
|
37404aac5c |
@@ -1,57 +0,0 @@
|
|||||||
name: ActivityPub Federation Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- "activitypub/**"
|
|
||||||
- "webserver/**"
|
|
||||||
- "test/automated/activitypub/**"
|
|
||||||
- ".github/workflows/activitypub-federation-tests.yaml"
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "activitypub/**"
|
|
||||||
- "webserver/**"
|
|
||||||
- "test/automated/activitypub/**"
|
|
||||||
- ".github/workflows/activitypub-federation-tests.yaml"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
federation-test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- id: skip_check
|
|
||||||
uses: fkirc/skip-duplicate-actions@v5
|
|
||||||
with:
|
|
||||||
concurrent_skipping: "same_content_newer"
|
|
||||||
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version: "^1.23"
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Install Caddy
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl
|
|
||||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
|
||||||
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y caddy
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get install -y snac2 ffmpeg
|
|
||||||
|
|
||||||
- name: Run setup
|
|
||||||
run: |
|
|
||||||
cd test/automated/activitypub
|
|
||||||
sudo ./setup.sh
|
|
||||||
|
|
||||||
- name: Run federation test
|
|
||||||
run: |
|
|
||||||
cd test/automated/activitypub
|
|
||||||
CI=true USER_COUNT=20 ./run.sh
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
name: ActivityPub Follower Validation Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- "activitypub/**"
|
|
||||||
- "config/**"
|
|
||||||
- "main.go"
|
|
||||||
- "test/automated/activitypub/**"
|
|
||||||
- ".github/workflows/activitypub-follower-validation-tests.yaml"
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "activitypub/**"
|
|
||||||
- "config/**"
|
|
||||||
- "main.go"
|
|
||||||
- "test/automated/activitypub/**"
|
|
||||||
- ".github/workflows/activitypub-follower-validation-tests.yaml"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
follower-validation-test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- id: skip_check
|
|
||||||
uses: fkirc/skip-duplicate-actions@v5
|
|
||||||
with:
|
|
||||||
concurrent_skipping: "same_content_newer"
|
|
||||||
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version: "^1.23"
|
|
||||||
cache: true
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y sqlite3 ffmpeg jq
|
|
||||||
|
|
||||||
- name: Run follower validation test
|
|
||||||
timeout-minutes: 15
|
|
||||||
run: |
|
|
||||||
cd test/automated/activitypub
|
|
||||||
./test-follower-validation.sh
|
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
name: ActivityPub Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- "activitypub/**"
|
||||||
|
- "core/chat/**"
|
||||||
|
- "config/**"
|
||||||
|
- "main.go"
|
||||||
|
- "webserver/**"
|
||||||
|
- "test/automated/activitypub/**"
|
||||||
|
- ".github/workflows/activitypub-tests.yaml"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "activitypub/**"
|
||||||
|
- "core/chat/**"
|
||||||
|
- "config/**"
|
||||||
|
- "main.go"
|
||||||
|
- "webserver/**"
|
||||||
|
- "test/automated/activitypub/**"
|
||||||
|
- ".github/workflows/activitypub-tests.yaml"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
federation-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- id: skip_check
|
||||||
|
uses: fkirc/skip-duplicate-actions@v5
|
||||||
|
with:
|
||||||
|
concurrent_skipping: "same_content_newer"
|
||||||
|
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version: "^1.23"
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Install Caddy
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl
|
||||||
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||||
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y caddy
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get install -y snac2 ffmpeg
|
||||||
|
|
||||||
|
- name: Run setup
|
||||||
|
run: |
|
||||||
|
cd test/automated/activitypub
|
||||||
|
sudo ./setup.sh
|
||||||
|
|
||||||
|
- name: Run federation test
|
||||||
|
run: |
|
||||||
|
cd test/automated/activitypub
|
||||||
|
CI=true USER_COUNT=20 ./test-federation.sh
|
||||||
|
|
||||||
|
chat-sanitization-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- id: skip_check
|
||||||
|
uses: fkirc/skip-duplicate-actions@v5
|
||||||
|
with:
|
||||||
|
concurrent_skipping: "same_content_newer"
|
||||||
|
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version: "^1.23"
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Install Caddy
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl
|
||||||
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
|
||||||
|
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y caddy
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get install -y snac2 ffmpeg
|
||||||
|
|
||||||
|
- name: Run setup
|
||||||
|
run: |
|
||||||
|
cd test/automated/activitypub
|
||||||
|
sudo ./setup.sh
|
||||||
|
|
||||||
|
- name: Run chat sanitization test
|
||||||
|
run: |
|
||||||
|
cd test/automated/activitypub
|
||||||
|
CI=true ./test-chat-sanitization.sh
|
||||||
|
|
||||||
|
follower-validation-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- id: skip_check
|
||||||
|
uses: fkirc/skip-duplicate-actions@v5
|
||||||
|
with:
|
||||||
|
concurrent_skipping: "same_content_newer"
|
||||||
|
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version: "^1.23"
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y sqlite3 ffmpeg jq
|
||||||
|
|
||||||
|
- name: Run follower validation test
|
||||||
|
timeout-minutes: 15
|
||||||
|
run: |
|
||||||
|
cd test/automated/activitypub
|
||||||
|
./test-follower-validation.sh
|
||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
steps:
|
steps:
|
||||||
- name: Add comment
|
- name: Add comment
|
||||||
uses: peter-evans/create-or-update-comment@9143c495e69aa6ad64fa02b7515ccab6bd35aad1
|
uses: peter-evans/create-or-update-comment@57232238742e38b2ccc27136ce596ccae7ca28b4
|
||||||
with:
|
with:
|
||||||
issue-number: ${{ github.event.issue.number }}
|
issue-number: ${{ github.event.issue.number }}
|
||||||
body: |
|
body: |
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish to Chromatic
|
- name: Publish to Chromatic
|
||||||
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' && steps.changed-files-yaml.outputs.src_any_changed == 'true' }}
|
||||||
uses: chromaui/action@v15
|
uses: chromaui/action@v16
|
||||||
with:
|
with:
|
||||||
workingDir: web
|
workingDir: web
|
||||||
projectToken: f47410569b62
|
projectToken: f47410569b62
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
formatting:
|
formatting:
|
||||||
name: Code formatting
|
name: Code formatting
|
||||||
|
if: github.actor != 'renovate[bot]' && github.actor != 'dependabot[bot]'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -133,6 +134,7 @@ jobs:
|
|||||||
|
|
||||||
unused-code:
|
unused-code:
|
||||||
name: Test for unused code
|
name: Test for unused code
|
||||||
|
if: github.actor != 'renovate[bot]' && github.actor != 'dependabot[bot]'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -197,7 +199,7 @@ jobs:
|
|||||||
web-bundle:
|
web-bundle:
|
||||||
name: Build and bundle web project
|
name: Build and bundle web project
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'owncast/owncast'
|
if: github.repository == 'owncast/owncast' && !cancelled()
|
||||||
needs: [formatting, unused-code]
|
needs: [formatting, unused-code]
|
||||||
steps:
|
steps:
|
||||||
- id: skip_check
|
- id: skip_check
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate-checklist:
|
validate-checklist:
|
||||||
@@ -44,6 +45,7 @@ jobs:
|
|||||||
}
|
}
|
||||||
|
|
||||||
- name: Validate required checkboxes
|
- name: Validate required checkboxes
|
||||||
|
id: validate
|
||||||
if: ${{ steps.membership.outputs.result == 'false' }}
|
if: ${{ steps.membership.outputs.result == 'false' }}
|
||||||
uses: actions/github-script@v8
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
@@ -55,6 +57,7 @@ jobs:
|
|||||||
const match = prBody.match(markerPattern);
|
const match = prBody.match(markerPattern);
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
|
core.setOutput('missing_checklist', 'true');
|
||||||
core.setFailed(
|
core.setFailed(
|
||||||
'## PR Checklist Validation Failed\n\n' +
|
'## PR Checklist Validation Failed\n\n' +
|
||||||
'The required checklist section is missing from your PR description.\n\n' +
|
'The required checklist section is missing from your PR description.\n\n' +
|
||||||
@@ -86,3 +89,24 @@ jobs:
|
|||||||
} else {
|
} else {
|
||||||
console.log('All required checklist items are checked');
|
console.log('All required checklist items are checked');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- name: Comment on PR about missing checklist
|
||||||
|
if: ${{ steps.validate.outputs.missing_checklist == 'true' }}
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const body =
|
||||||
|
'## PR Checklist Missing\n\n' +
|
||||||
|
'It looks like the **required checklist section** was removed from your PR description. ' +
|
||||||
|
'This section is needed for the PR checks to pass.\n\n' +
|
||||||
|
'Please edit your PR description and restore the checklist from ' +
|
||||||
|
'[the PR template](https://raw.githubusercontent.com/owncast/owncast/refs/heads/develop/.github/PULL_REQUEST_TEMPLATE.MD). ' +
|
||||||
|
'Once restored, check off each item to confirm you have completed them.\n\n' +
|
||||||
|
'The checklist section begins with `<!-- REQUIRED-CHECKLIST:START -->` and ends with `<!-- REQUIRED-CHECKLIST:END -->`.';
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.payload.pull_request.number,
|
||||||
|
body: body
|
||||||
|
});
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ on:
|
|||||||
# Run the workflow every hour
|
# Run the workflow every hour
|
||||||
- cron: '0 * * * *'
|
- cron: '0 * * * *'
|
||||||
push:
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
paths:
|
paths:
|
||||||
- 'web/i18n/en/translation.json'
|
- 'web/i18n/en/translation.json'
|
||||||
- 'web/**/*.tsx'
|
- 'web/**/*.tsx'
|
||||||
|
|||||||
@@ -4,12 +4,24 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-fed/activity/streams/vocab"
|
"github.com/go-fed/activity/streams/vocab"
|
||||||
|
"github.com/microcosm-cc/bluemonday"
|
||||||
"github.com/owncast/owncast/activitypub/resolvers"
|
"github.com/owncast/owncast/activitypub/resolvers"
|
||||||
"github.com/owncast/owncast/core/chat"
|
"github.com/owncast/owncast/core/chat"
|
||||||
"github.com/owncast/owncast/core/chat/events"
|
"github.com/owncast/owncast/core/chat/events"
|
||||||
"github.com/owncast/owncast/persistence/configrepository"
|
"github.com/owncast/owncast/persistence/configrepository"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// sanitizeActorName strips HTML tags from the ActivityPub actor display name.
|
||||||
|
// Falls back to the username if the display name is empty or entirely HTML.
|
||||||
|
func sanitizeActorName(displayName, username string) string {
|
||||||
|
strict := bluemonday.StrictPolicy()
|
||||||
|
name := strict.Sanitize(displayName)
|
||||||
|
if name == "" {
|
||||||
|
name = strict.Sanitize(username)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
func handleEngagementActivity(eventType events.EventType, isLiveNotification bool, actorReference vocab.ActivityStreamsActorProperty, action string) error {
|
func handleEngagementActivity(eventType events.EventType, isLiveNotification bool, actorReference vocab.ActivityStreamsActorProperty, action string) error {
|
||||||
configRepository := configrepository.Get()
|
configRepository := configrepository.Get()
|
||||||
|
|
||||||
@@ -30,10 +42,7 @@ func handleEngagementActivity(eventType events.EventType, isLiveNotification boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send chat message
|
// Send chat message
|
||||||
actorName := actor.Name
|
actorName := sanitizeActorName(actor.Name, actor.Username)
|
||||||
if actorName == "" {
|
|
||||||
actorName = actor.Username
|
|
||||||
}
|
|
||||||
actorIRI := actor.ActorIriString()
|
actorIRI := actor.ActorIriString()
|
||||||
|
|
||||||
userPrefix := fmt.Sprintf("%s ", actorName)
|
userPrefix := fmt.Sprintf("%s ", actorName)
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package inbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSanitizeActorName(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
displayName string
|
||||||
|
username string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "plain display name",
|
||||||
|
displayName: "Alice",
|
||||||
|
username: "alice",
|
||||||
|
expected: "Alice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "display name with emoji",
|
||||||
|
displayName: "Alice 🦊",
|
||||||
|
username: "alice",
|
||||||
|
expected: "Alice 🦊",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "display name with unicode",
|
||||||
|
displayName: "Ålice Böb",
|
||||||
|
username: "alice",
|
||||||
|
expected: "Ålice Böb",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty display name falls back to username",
|
||||||
|
displayName: "",
|
||||||
|
username: "alice",
|
||||||
|
expected: "alice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "script tag in display name",
|
||||||
|
displayName: `<script>alert("xss")</script>`,
|
||||||
|
username: "alice",
|
||||||
|
expected: "alice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "iframe injection in display name",
|
||||||
|
displayName: `<iframe src="https://evil.com" style="position:fixed;top:0;left:0;width:100%;height:100%"></iframe>`,
|
||||||
|
username: "alice",
|
||||||
|
expected: "alice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "img tag in display name",
|
||||||
|
displayName: `<img src="https://evil.com/track.png">`,
|
||||||
|
username: "alice",
|
||||||
|
expected: "alice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "form injection in display name",
|
||||||
|
displayName: `<form action="https://evil.com/steal"><input name="pw" type="password"></form>`,
|
||||||
|
username: "alice",
|
||||||
|
expected: "alice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "meta refresh in display name",
|
||||||
|
displayName: `<meta http-equiv="refresh" content="0;url=https://evil.com">`,
|
||||||
|
username: "alice",
|
||||||
|
expected: "alice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mixed text and HTML in display name",
|
||||||
|
displayName: `Alice <script>alert(1)</script> Bob`,
|
||||||
|
username: "alice",
|
||||||
|
expected: "Alice Bob",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom emoji HTML in display name",
|
||||||
|
displayName: `Alice :blobcat: <img src="https://instance.com/emoji/blobcat.png" class="custom-emoji">`,
|
||||||
|
username: "alice",
|
||||||
|
expected: "Alice :blobcat: ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HTML in both display name and username",
|
||||||
|
displayName: `<script>alert(1)</script>`,
|
||||||
|
username: `<b>alice</b>`,
|
||||||
|
expected: "alice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "entirely HTML display name falls back to username",
|
||||||
|
displayName: `<div></div>`,
|
||||||
|
username: "alice",
|
||||||
|
expected: "alice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "style tag in display name",
|
||||||
|
displayName: `<style>body{display:none}</style>Alice`,
|
||||||
|
username: "alice",
|
||||||
|
expected: "Alice",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested HTML tags",
|
||||||
|
displayName: `<div><span><a href="https://evil.com">Click me</a></span></div>`,
|
||||||
|
username: "alice",
|
||||||
|
expected: "Click me",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "event handler attributes",
|
||||||
|
displayName: `<img src=x onerror="alert(1)">Alice`,
|
||||||
|
username: "alice",
|
||||||
|
expected: "Alice",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := sanitizeActorName(tt.displayName, tt.username)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("sanitizeActorName(%q, %q) = %q, want %q", tt.displayName, tt.username, result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -126,7 +126,6 @@ func SendFediverseAction(eventType string, userAccountName string, image *string
|
|||||||
}
|
}
|
||||||
|
|
||||||
message.SetDefaults()
|
message.SetDefaults()
|
||||||
message.RenderBody()
|
|
||||||
|
|
||||||
if err := Broadcast(&message); err != nil {
|
if err := Broadcast(&message); err != nil {
|
||||||
log.Errorln("error sending system message", err)
|
log.Errorln("error sending system message", err)
|
||||||
|
|||||||
+10
-41
@@ -1,7 +1,6 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -99,53 +98,23 @@ func createInitialOfflineState() error {
|
|||||||
// offline video stream state only. No live stream HLS segments will continue to be
|
// offline video stream state only. No live stream HLS segments will continue to be
|
||||||
// referenced.
|
// referenced.
|
||||||
func transitionToOfflineVideoStreamContent() {
|
func transitionToOfflineVideoStreamContent() {
|
||||||
log.Traceln("Placing offline fMP4 content into HLS directories")
|
log.Traceln("Firing transcoder with offline stream state")
|
||||||
|
|
||||||
configRepository := configrepository.Get()
|
_transcoder := transcoder.NewTranscoder()
|
||||||
|
_transcoder.SetIdentifier("offline")
|
||||||
|
_transcoder.SetLatencyLevel(models.GetLatencyLevel(4))
|
||||||
|
_transcoder.SetIsEvent(true)
|
||||||
|
|
||||||
offlineInitPath, offlineSegmentPath, err := saveOfflineFMP4ToDisk()
|
offlineFilePath, err := saveOfflineClipToDisk("offline-v2.ts")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("unable to save offline fMP4 files:", err)
|
log.Fatalln("unable to save offline clip:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
variants := configRepository.GetStreamOutputVariants()
|
_transcoder.SetInput(offlineFilePath)
|
||||||
if len(variants) == 0 {
|
go _transcoder.Start(false)
|
||||||
// Ensure at least one variant directory exists.
|
|
||||||
variants = make([]models.StreamOutputVariant, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
for index := range variants {
|
|
||||||
variantDir := filepath.Join(config.HLSStoragePath, fmt.Sprintf("%d", index))
|
|
||||||
if err := os.MkdirAll(variantDir, 0o750); err != nil {
|
|
||||||
log.Errorln("unable to create variant directory:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
makeVariantIndexOffline(index, offlineInitPath, offlineSegmentPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the master playlist that points to each variant's playlist.
|
|
||||||
masterPlaylistPath := filepath.Join(config.HLSStoragePath, "stream.m3u8")
|
|
||||||
masterFile, err := os.Create(masterPlaylistPath) //nolint:gosec
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("unable to create master playlist:", err)
|
|
||||||
} else {
|
|
||||||
_, _ = masterFile.WriteString("#EXTM3U\n")
|
|
||||||
_, _ = masterFile.WriteString("#EXT-X-VERSION:7\n")
|
|
||||||
_, _ = masterFile.WriteString("#EXT-X-INDEPENDENT-SEGMENTS\n")
|
|
||||||
for index := range variants {
|
|
||||||
_, _ = fmt.Fprintf(masterFile, "#EXT-X-STREAM-INF:BANDWIDTH=0\n")
|
|
||||||
_, _ = fmt.Fprintf(masterFile, "%d/stream.m3u8\n", index)
|
|
||||||
}
|
|
||||||
if err := masterFile.Close(); err != nil {
|
|
||||||
log.Errorln("unable to close master playlist:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify the storage provider so it can rewrite variant URLs
|
|
||||||
// to absolute paths for S3 or remote serving endpoints.
|
|
||||||
_storage.MasterPlaylistWritten(masterPlaylistPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the logo to be the thumbnail
|
// Copy the logo to be the thumbnail
|
||||||
|
configRepository := configrepository.Get()
|
||||||
logo := configRepository.GetLogoPath()
|
logo := configRepository.GetLogoPath()
|
||||||
dst := filepath.Join(config.TempDir, "thumbnail.jpg")
|
dst := filepath.Join(config.TempDir, "thumbnail.jpg")
|
||||||
if err = utils.Copy(filepath.Join("data", logo), dst); err != nil {
|
if err = utils.Copy(filepath.Join("data", logo), dst); err != nil {
|
||||||
|
|||||||
+33
-61
@@ -5,13 +5,14 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/grafov/m3u8"
|
||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/static"
|
"github.com/owncast/owncast/static"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func appendOfflineToVariantPlaylist(index int, playlistFilePath string, initFilename string, segmentFilename string) {
|
func appendOfflineToVariantPlaylist(index int, playlistFilePath string) {
|
||||||
existingPlaylistContents, err := os.ReadFile(playlistFilePath) // nolint: gosec
|
existingPlaylistContents, err := os.ReadFile(playlistFilePath) // nolint: gosec
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln("unable to read existing playlist file", err)
|
log.Debugln("unable to read existing playlist file", err)
|
||||||
@@ -33,10 +34,9 @@ func appendOfflineToVariantPlaylist(index int, playlistFilePath string, initFile
|
|||||||
|
|
||||||
// Manually append the offline clip to the end of the media playlist.
|
// Manually append the offline clip to the end of the media playlist.
|
||||||
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-DISCONTINUITY\n")
|
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-DISCONTINUITY\n")
|
||||||
_, _ = fmt.Fprintf(atomicWriteTmpPlaylistFile, "#EXT-X-MAP:URI=\"%s\"\n", initFilename)
|
|
||||||
// If "offline" content gets changed then change the duration below
|
// If "offline" content gets changed then change the duration below
|
||||||
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXTINF:8.000000,\n")
|
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXTINF:8.000000,\n")
|
||||||
_, _ = fmt.Fprintf(atomicWriteTmpPlaylistFile, "%s\n", segmentFilename)
|
_, _ = atomicWriteTmpPlaylistFile.WriteString("offline-v2.ts\n")
|
||||||
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-ENDLIST\n")
|
_, _ = atomicWriteTmpPlaylistFile.WriteString("#EXT-X-ENDLIST\n")
|
||||||
|
|
||||||
if err := atomicWriteTmpPlaylistFile.Close(); err != nil {
|
if err := atomicWriteTmpPlaylistFile.Close(); err != nil {
|
||||||
@@ -48,89 +48,61 @@ func appendOfflineToVariantPlaylist(index int, playlistFilePath string, initFile
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeVariantIndexOffline(index int, offlineInitPath string, offlineSegmentPath string) {
|
func makeVariantIndexOffline(index int, offlineFilePath string, offlineFilename string) {
|
||||||
variantDir := filepath.Join(config.HLSStoragePath, fmt.Sprintf("%d", index))
|
playlistFilePath := fmt.Sprintf(filepath.Join(config.HLSStoragePath, "%d/stream.m3u8"), index)
|
||||||
playlistFilePath := filepath.Join(variantDir, "stream.m3u8")
|
segmentFilePath := fmt.Sprintf(filepath.Join(config.HLSStoragePath, "%d/%s"), index, offlineFilename)
|
||||||
|
|
||||||
initDest := filepath.Join(variantDir, "offline-init.mp4")
|
if err := utils.Copy(offlineFilePath, segmentFilePath); err != nil {
|
||||||
segmentDest := filepath.Join(variantDir, "offline-v2.m4s")
|
|
||||||
|
|
||||||
if err := utils.Copy(offlineInitPath, initDest); err != nil {
|
|
||||||
log.Warnln(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := _storage.Save(initDest, 0); err != nil {
|
|
||||||
log.Warnln(err)
|
log.Warnln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utils.Copy(offlineSegmentPath, segmentDest); err != nil {
|
if _, err := _storage.Save(segmentFilePath, 0); err != nil {
|
||||||
log.Warnln(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := _storage.Save(segmentDest, 0); err != nil {
|
|
||||||
log.Warnln(err)
|
log.Warnln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if utils.DoesFileExists(playlistFilePath) {
|
if utils.DoesFileExists(playlistFilePath) {
|
||||||
appendOfflineToVariantPlaylist(index, playlistFilePath, "offline-init.mp4", "offline-v2.m4s")
|
appendOfflineToVariantPlaylist(index, playlistFilePath)
|
||||||
} else {
|
} else {
|
||||||
createEmptyOfflinePlaylist(playlistFilePath, "offline-init.mp4", "offline-v2.m4s")
|
createEmptyOfflinePlaylist(playlistFilePath, offlineFilename)
|
||||||
}
|
}
|
||||||
if _, err := _storage.Save(playlistFilePath, 0); err != nil {
|
if _, err := _storage.Save(playlistFilePath, 0); err != nil {
|
||||||
log.Warnln(err)
|
log.Warnln(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createEmptyOfflinePlaylist(playlistFilePath string, initFilename string, segmentFilename string) {
|
func createEmptyOfflinePlaylist(playlistFilePath string, offlineFilename string) {
|
||||||
|
p, err := m3u8.NewMediaPlaylist(1, 1)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If "offline" content gets changed then change the duration below
|
||||||
|
if err := p.Append(offlineFilename, 8.0, ""); err != nil {
|
||||||
|
log.Errorln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Close()
|
||||||
f, err := os.Create(playlistFilePath) //nolint:gosec
|
f, err := os.Create(playlistFilePath) //nolint:gosec
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
if _, err := f.Write(p.Encode().Bytes()); err != nil {
|
||||||
_, _ = f.WriteString("#EXTM3U\n")
|
log.Errorln(err)
|
||||||
_, _ = f.WriteString("#EXT-X-VERSION:7\n")
|
}
|
||||||
_, _ = f.WriteString("#EXT-X-TARGETDURATION:8\n")
|
|
||||||
_, _ = fmt.Fprintf(f, "#EXT-X-MAP:URI=\"%s\"\n", initFilename)
|
|
||||||
// If "offline" content gets changed then change the duration below
|
|
||||||
_, _ = f.WriteString("#EXTINF:8.000000,\n")
|
|
||||||
_, _ = fmt.Fprintf(f, "%s\n", segmentFilename)
|
|
||||||
_, _ = f.WriteString("#EXT-X-ENDLIST\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveOfflineFMP4ToDisk() (initPath string, segmentPath string, err error) {
|
func saveOfflineClipToDisk(offlineFilename string) (string, error) {
|
||||||
initData := static.GetOfflineInitSegment()
|
offlineFileData := static.GetOfflineSegment()
|
||||||
initTmp, err := os.CreateTemp(config.TempDir, "offline-init-*.mp4")
|
offlineTmpFile, err := os.CreateTemp(config.TempDir, offlineFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("unable to create temp file for offline init segment: %s", err)
|
log.Errorln("unable to create temp file for offline video segment", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = initTmp.Write(initData); err != nil {
|
if _, err = offlineTmpFile.Write(offlineFileData); err != nil {
|
||||||
return "", "", fmt.Errorf("unable to write offline init segment to disk: %s", err)
|
return "", fmt.Errorf("unable to write offline segment to disk: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := initTmp.Close(); err != nil {
|
offlineFilePath := offlineTmpFile.Name()
|
||||||
return "", "", fmt.Errorf("unable to close offline init segment: %s", err)
|
return filepath.Abs(offlineFilePath)
|
||||||
}
|
|
||||||
initPath, _ = filepath.Abs(initTmp.Name())
|
|
||||||
|
|
||||||
segData := static.GetOfflineMediaSegment()
|
|
||||||
segTmp, err := os.CreateTemp(config.TempDir, "offline-v2-*.m4s")
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("unable to create temp file for offline media segment: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = segTmp.Write(segData); err != nil {
|
|
||||||
return "", "", fmt.Errorf("unable to write offline media segment to disk: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := segTmp.Close(); err != nil {
|
|
||||||
return "", "", fmt.Errorf("unable to close offline media segment: %s", err)
|
|
||||||
}
|
|
||||||
segmentPath, _ = filepath.Abs(segTmp.Name())
|
|
||||||
|
|
||||||
return initPath, segmentPath, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ func getAllFilesRecursive(baseDirectory string) (map[string][]os.FileInfo, error
|
|||||||
directory = info.Name()
|
directory = info.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
if filepath.Ext(info.Name()) == ".m4s" {
|
if filepath.Ext(info.Name()) == ".ts" {
|
||||||
files[directory] = append(files[directory], info)
|
files[directory] = append(files[directory], info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ func (s *S3Storage) retrieveAllVideoSegments() ([]s3object, error) {
|
|||||||
err := s.s3Client.ListObjectsPages(allObjectsListRequest,
|
err := s.s3Client.ListObjectsPages(allObjectsListRequest,
|
||||||
func(page *s3.ListObjectsOutput, lastPage bool) bool {
|
func(page *s3.ListObjectsOutput, lastPage bool) bool {
|
||||||
for _, item := range page.Contents {
|
for _, item := range page.Contents {
|
||||||
if strings.HasSuffix(*item.Key, ".m4s") {
|
if strings.HasSuffix(*item.Key, ".ts") {
|
||||||
allObjects = append(allObjects, s3object{
|
allObjects = append(allObjects, s3object{
|
||||||
key: *item.Key,
|
key: *item.Key,
|
||||||
lastModified: *item.LastModified,
|
lastModified: *item.LastModified,
|
||||||
|
|||||||
+4
-2
@@ -96,7 +96,9 @@ func SetStreamAsDisconnected() {
|
|||||||
_stats.LastConnectTime = nil
|
_stats.LastConnectTime = nil
|
||||||
_broadcaster = nil
|
_broadcaster = nil
|
||||||
|
|
||||||
offlineInitPath, offlineSegmentPath, err := saveOfflineFMP4ToDisk()
|
offlineFilename := "offline-v2.ts"
|
||||||
|
|
||||||
|
offlineFilePath, err := saveOfflineClipToDisk(offlineFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
return
|
return
|
||||||
@@ -120,7 +122,7 @@ func SetStreamAsDisconnected() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for index := range _currentBroadcast.OutputSettings {
|
for index := range _currentBroadcast.OutputSettings {
|
||||||
makeVariantIndexOffline(index, offlineInitPath, offlineSegmentPath)
|
makeVariantIndexOffline(index, offlineFilePath, offlineFilename)
|
||||||
}
|
}
|
||||||
|
|
||||||
StartOfflineCleanupTimer()
|
StartOfflineCleanupTimer()
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ type FileWriterReceiverServiceCallback interface {
|
|||||||
SegmentWritten(localFilePath string)
|
SegmentWritten(localFilePath string)
|
||||||
VariantPlaylistWritten(localFilePath string)
|
VariantPlaylistWritten(localFilePath string)
|
||||||
MasterPlaylistWritten(localFilePath string)
|
MasterPlaylistWritten(localFilePath string)
|
||||||
InitSegmentWritten(localFilePath string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileWriterReceiverService accepts transcoder responses via HTTP and fires the callbacks.
|
// FileWriterReceiverService accepts transcoder responses via HTTP and fires the callbacks.
|
||||||
@@ -82,10 +81,8 @@ func (s *FileWriterReceiverService) uploadHandler(w http.ResponseWriter, r *http
|
|||||||
func (s *FileWriterReceiverService) fileWritten(path string) {
|
func (s *FileWriterReceiverService) fileWritten(path string) {
|
||||||
if utils.GetRelativePathFromAbsolutePath(path) == "hls/stream.m3u8" {
|
if utils.GetRelativePathFromAbsolutePath(path) == "hls/stream.m3u8" {
|
||||||
s.callbacks.MasterPlaylistWritten(path)
|
s.callbacks.MasterPlaylistWritten(path)
|
||||||
} else if strings.HasSuffix(path, ".m4s") {
|
} else if strings.HasSuffix(path, ".ts") {
|
||||||
s.callbacks.SegmentWritten(path)
|
s.callbacks.SegmentWritten(path)
|
||||||
} else if strings.HasSuffix(path, "init.mp4") {
|
|
||||||
s.callbacks.InitSegmentWritten(path)
|
|
||||||
} else if strings.HasSuffix(path, ".m3u8") {
|
} else if strings.HasSuffix(path, ".m3u8") {
|
||||||
s.callbacks.VariantPlaylistWritten(path)
|
s.callbacks.VariantPlaylistWritten(path)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,3 @@ func (h *HLSHandler) VariantPlaylistWritten(localFilePath string) {
|
|||||||
func (h *HLSHandler) MasterPlaylistWritten(localFilePath string) {
|
func (h *HLSHandler) MasterPlaylistWritten(localFilePath string) {
|
||||||
h.Storage.MasterPlaylistWritten(localFilePath)
|
h.Storage.MasterPlaylistWritten(localFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitSegmentWritten is fired when an fMP4 init segment is written to disk.
|
|
||||||
func (h *HLSHandler) InitSegmentWritten(localFilePath string) {
|
|
||||||
h.Storage.SegmentWritten(localFilePath)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package transcoder
|
package transcoder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
@@ -65,7 +64,7 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
|
|||||||
var modTime time.Time
|
var modTime time.Time
|
||||||
var names []string
|
var names []string
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if path.Ext(f.Name()) != ".m4s" {
|
if path.Ext(f.Name()) != ".ts" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,21 +88,7 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
configRepository := configrepository.Get()
|
configRepository := configrepository.Get()
|
||||||
// With multiple variants ffmpeg names init segments init_0.mp4, init_1.mp4, etc.
|
|
||||||
// With a single variant it uses init.mp4.
|
|
||||||
initSegment := path.Join(framePath, fmt.Sprintf("init_%d.mp4", variantIndex))
|
|
||||||
if !utils.DoesFileExists(initSegment) {
|
|
||||||
initSegment = path.Join(framePath, "init.mp4")
|
|
||||||
}
|
|
||||||
mostRecentFile := path.Join(framePath, names[0])
|
mostRecentFile := path.Join(framePath, names[0])
|
||||||
|
|
||||||
// The segment may have been cleaned up between ReadDir and now.
|
|
||||||
if !utils.DoesFileExists(mostRecentFile) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// fMP4 segments require the init segment to be readable by ffmpeg
|
|
||||||
inputArg := fmt.Sprintf("concat:%s|%s", initSegment, mostRecentFile)
|
|
||||||
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
|
ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath())
|
||||||
outputFileTemp := path.Join(config.TempDir, "tempthumbnail.jpg")
|
outputFileTemp := path.Join(config.TempDir, "tempthumbnail.jpg")
|
||||||
|
|
||||||
@@ -111,7 +96,7 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
|
|||||||
"-y", // Overwrite file
|
"-y", // Overwrite file
|
||||||
"-threads", "1", // Low priority processing
|
"-threads", "1", // Low priority processing
|
||||||
"-t", "1", // Pull from frame 1
|
"-t", "1", // Pull from frame 1
|
||||||
"-i", inputArg, // Input (init segment + media segment)
|
"-i", mostRecentFile, // Input
|
||||||
"-f", "image2", // format
|
"-f", "image2", // format
|
||||||
"-vframes", "1", // Single frame
|
"-vframes", "1", // Single frame
|
||||||
outputFileTemp,
|
outputFileTemp,
|
||||||
@@ -126,7 +111,7 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error {
|
|||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
makeAnimatedGifPreview(inputArg, previewGifFile)
|
makeAnimatedGifPreview(mostRecentFile, previewGifFile)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,9 +245,7 @@ func (t *Transcoder) getFlags() *execInfo {
|
|||||||
ffmpegFlags = append(ffmpegFlags, hlsOptionsString...)
|
ffmpegFlags = append(ffmpegFlags, hlsOptionsString...)
|
||||||
ffmpegFlags = append(ffmpegFlags, hlsEventString...)
|
ffmpegFlags = append(ffmpegFlags, hlsEventString...)
|
||||||
ffmpegFlags = append(ffmpegFlags, []string{
|
ffmpegFlags = append(ffmpegFlags, []string{
|
||||||
"-hls_segment_type", "fmp4",
|
"-segment_format_options", "mpegts_flags=mpegts_copyts=1",
|
||||||
"-hls_fmp4_init_filename", "init.mp4",
|
|
||||||
"-bsf:a", "aac_adtstoasc", // Convert AAC ADTS headers to ASC for fMP4 container compatibility
|
|
||||||
}...)
|
}...)
|
||||||
ffmpegFlags = append(ffmpegFlags, t.codec.ExtraArguments()...)
|
ffmpegFlags = append(ffmpegFlags, t.codec.ExtraArguments()...)
|
||||||
|
|
||||||
@@ -259,7 +257,7 @@ func (t *Transcoder) getFlags() *execInfo {
|
|||||||
// Filenames
|
// Filenames
|
||||||
"-master_pl_name", "stream.m3u8",
|
"-master_pl_name", "stream.m3u8",
|
||||||
|
|
||||||
"-hls_segment_filename", localListenerAddress + "/%v/stream-" + t.segmentIdentifier + "-%d.m4s", // Send HLS segments back to us over HTTP
|
"-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
|
"-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
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegNvencCommand(t *testing.T) {
|
|||||||
cmd := transcoder.GetString()
|
cmd := transcoder.GetString()
|
||||||
|
|
||||||
expectedLogPath := "data/logs/transcoder.log"
|
expectedLogPath := "data/logs/transcoder.log"
|
||||||
expected := `FFREPORT=file=` + expectedLogPath + `:level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -hwaccel cuda -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_nvenc -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -tune:v:0 ll -map a:0? -c:a:0 copy -preset p3 -map v:0 -c:v:1 h264_nvenc -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -tune:v:1 ll -map a:0? -c:a:1 copy -preset p5 -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset p1 -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 -hls_segment_type fmp4 -hls_fmp4_init_filename init.mp4 -bsf:a aac_adtstoasc -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdoieGg-%d.m4s -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 cuda -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_nvenc -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -tune:v:0 ll -map a:0? -c:a:0 copy -preset p3 -map v:0 -c:v:1 h264_nvenc -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -tune:v:1 ll -map a:0? -c:a:1 copy -preset p5 -map v:0 -c:v:2 copy -map a:0? -c:a:2 copy -preset p1 -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 yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdoieGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
|
||||||
|
|
||||||
if cmd != expected {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegOmxCommand(t *testing.T) {
|
|||||||
cmd := transcoder.GetString()
|
cmd := transcoder.GetString()
|
||||||
|
|
||||||
expectedLogPath := "data/logs/transcoder.log"
|
expectedLogPath := "data/logs/transcoder.log"
|
||||||
expected := `FFREPORT=file=` + expectedLogPath + `:level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_omx -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_omx -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 -hls_segment_type fmp4 -hls_fmp4_init_filename init.mp4 -bsf:a aac_adtstoasc -tune zerolatency -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.m4s -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 -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_omx -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_omx -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 -tune zerolatency -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
|
||||||
|
|
||||||
if cmd != expected {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegQuicksyncCommand(t *testing.T) {
|
|||||||
cmd := transcoder.GetString()
|
cmd := transcoder.GetString()
|
||||||
|
|
||||||
expectedLogPath := "data/logs/transcoder.log"
|
expectedLogPath := "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 -hls_segment_type fmp4 -hls_fmp4_init_filename init.mp4 -bsf:a aac_adtstoasc -pix_fmt qsv -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.m4s -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 -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 {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegVaapiCommand(t *testing.T) {
|
|||||||
cmd := transcoder.GetString()
|
cmd := transcoder.GetString()
|
||||||
|
|
||||||
expectedLogPath := "data/logs/transcoder.log"
|
expectedLogPath := "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 -hls_segment_type fmp4 -hls_fmp4_init_filename init.mp4 -bsf:a aac_adtstoasc -pix_fmt vaapi -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.m4s -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 -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`
|
||||||
|
|
||||||
if cmd != expected {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegVideoToolboxCommand(t *testing.T) {
|
|||||||
cmd := transcoder.GetString()
|
cmd := transcoder.GetString()
|
||||||
|
|
||||||
expectedLogPath := "data/logs/transcoder.log"
|
expectedLogPath := "data/logs/transcoder.log"
|
||||||
expected := `FFREPORT=file=` + expectedLogPath + `:level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_videotoolbox -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -realtime true -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_videotoolbox -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 -hls_segment_type fmp4 -hls_fmp4_init_filename init.mp4 -bsf:a aac_adtstoasc -pix_fmt nv12 -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.m4s -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 -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 h264_videotoolbox -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -realtime true -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 h264_videotoolbox -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 nv12 -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdFsdfzGg-%d.ts -max_muxing_queue_size 400 -method PUT http://127.0.0.1:8123/%v/stream.m3u8`
|
||||||
|
|
||||||
if cmd != expected {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestFFmpegx264Command(t *testing.T) {
|
|||||||
cmd := transcoder.GetString()
|
cmd := transcoder.GetString()
|
||||||
|
|
||||||
expectedLogPath := "data/logs/transcoder.log"
|
expectedLogPath := "data/logs/transcoder.log"
|
||||||
expected := `FFREPORT=file=` + expectedLogPath + `:level=32 ` + transcoder.ffmpegPath + ` -hide_banner -loglevel warning -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 libx264 -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -x264-params:v:0 scenecut=0:open_gop=0 -bufsize:v:0 1088k -profile:v:0 high -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 libx264 -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -x264-params:v:1 scenecut=0:open_gop=0 -bufsize:v:1 3572k -profile:v:1 high -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 -hls_segment_type fmp4 -hls_fmp4_init_filename init.mp4 -bsf:a aac_adtstoasc -tune zerolatency -pix_fmt yuv420p -sc_threshold 0 -master_pl_name stream.m3u8 -hls_segment_filename http://127.0.0.1:8123/%v/stream-jdofFGg-%d.m4s -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 -fflags +genpts -flags +cgop -i fakecontent.flv -map v:0 -c:v:0 libx264 -b:v:0 1008k -maxrate:v:0 1088k -g:v:0 90 -keyint_min:v:0 90 -r:v:0 30 -x264-params:v:0 scenecut=0:open_gop=0 -bufsize:v:0 1088k -profile:v:0 high -map a:0? -c:a:0 copy -preset veryfast -map v:0 -c:v:1 libx264 -b:v:1 3308k -maxrate:v:1 3572k -g:v:1 72 -keyint_min:v:1 72 -r:v:1 24 -x264-params:v:1 scenecut=0:open_gop=0 -bufsize:v:1 3572k -profile:v:1 high -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 -tune zerolatency -pix_fmt yuv420p -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 {
|
if cmd != expected {
|
||||||
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
t.Errorf("ffmpeg command does not match expected.\nGot %s\n, want: %s", cmd, expected)
|
||||||
|
|||||||
@@ -16,25 +16,25 @@ require (
|
|||||||
github.com/hashicorp/go-retryablehttp v0.7.8
|
github.com/hashicorp/go-retryablehttp v0.7.8
|
||||||
github.com/jellydator/ttlcache/v3 v3.4.0
|
github.com/jellydator/ttlcache/v3 v3.4.0
|
||||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
||||||
github.com/mattn/go-sqlite3 v1.14.37
|
github.com/mattn/go-sqlite3 v1.14.40
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27
|
github.com/microcosm-cc/bluemonday v1.0.27
|
||||||
github.com/mssola/user_agent v0.6.0
|
github.com/mssola/user_agent v0.6.0
|
||||||
github.com/nakabonne/tstorage v0.3.6
|
github.com/nakabonne/tstorage v0.3.6
|
||||||
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590
|
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590
|
||||||
github.com/oapi-codegen/runtime v1.3.0
|
github.com/oapi-codegen/runtime v1.3.1
|
||||||
github.com/oschwald/geoip2-golang v1.13.0
|
github.com/oschwald/geoip2-golang v1.13.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/prometheus/client_golang v1.23.2
|
github.com/prometheus/client_golang v1.23.2
|
||||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
||||||
github.com/schollz/sqlite3dump v1.3.1
|
github.com/schollz/sqlite3dump v1.3.1
|
||||||
github.com/shirou/gopsutil/v4 v4.26.2
|
github.com/shirou/gopsutil/v4 v4.25.8
|
||||||
github.com/sirupsen/logrus v1.9.4
|
github.com/sirupsen/logrus v1.9.4
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569
|
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569
|
||||||
github.com/yuin/goldmark v1.7.13
|
github.com/yuin/goldmark v1.7.13
|
||||||
github.com/yuin/goldmark-emoji v1.0.6
|
github.com/yuin/goldmark-emoji v1.0.6
|
||||||
golang.org/x/crypto v0.49.0
|
golang.org/x/crypto v0.49.0
|
||||||
golang.org/x/mod v0.34.0
|
golang.org/x/mod v0.33.0
|
||||||
golang.org/x/net v0.51.0
|
golang.org/x/net v0.51.0
|
||||||
golang.org/x/time v0.15.0
|
golang.org/x/time v0.15.0
|
||||||
gopkg.in/evanphx/json-patch.v5 v5.9.11
|
gopkg.in/evanphx/json-patch.v5 v5.9.11
|
||||||
@@ -48,8 +48,7 @@ require (
|
|||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/ebitengine/purego v0.10.0 // indirect
|
github.com/ebitengine/purego v0.8.4 // indirect
|
||||||
github.com/fatih/color v1.19.0 // indirect
|
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
@@ -60,7 +59,6 @@ require (
|
|||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/lestrrat-go/strftime v1.1.1 // indirect
|
github.com/lestrrat-go/strftime v1.1.1 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/oschwald/maxminddb-golang v1.13.0 // indirect
|
github.com/oschwald/maxminddb-golang v1.13.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
@@ -68,16 +66,14 @@ require (
|
|||||||
github.com/prometheus/client_model v0.6.2 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.66.1 // indirect
|
github.com/prometheus/common v0.66.1 // indirect
|
||||||
github.com/prometheus/procfs v0.16.1 // indirect
|
github.com/prometheus/procfs v0.16.1 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.42.0 // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.35.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.8 // indirect
|
google.golang.org/protobuf v1.36.8 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ github.com/dave/jennifer v1.3.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhr
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
|
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||||
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
|
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
|
||||||
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||||
github.com/go-fed/httpsig v0.1.1-0.20190914113940-c2de3672e5b5/go.mod h1:T56HUNYZUQ1AGUzhAYPugZfp36sKApVnGBgKlIY+aIE=
|
github.com/go-fed/httpsig v0.1.1-0.20190914113940-c2de3672e5b5/go.mod h1:T56HUNYZUQ1AGUzhAYPugZfp36sKApVnGBgKlIY+aIE=
|
||||||
@@ -87,13 +87,19 @@ github.com/lestrrat-go/strftime v1.1.1 h1:zgf8QCsgj27GlKBy3SU9/8MMgegZ8UCzlCyHYr
|
|||||||
github.com/lestrrat-go/strftime v1.1.1/go.mod h1:YDrzHJAODYQ+xxvrn5SG01uFIQAeDTzpxNVppCz7Nmw=
|
github.com/lestrrat-go/strftime v1.1.1/go.mod h1:YDrzHJAODYQ+xxvrn5SG01uFIQAeDTzpxNVppCz7Nmw=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.37 h1:3DOZp4cXis1cUIpCfXLtmlGolNLp2VEqhiB/PARNBIg=
|
github.com/mattn/go-sqlite3 v1.14.37 h1:3DOZp4cXis1cUIpCfXLtmlGolNLp2VEqhiB/PARNBIg=
|
||||||
github.com/mattn/go-sqlite3 v1.14.37/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.37/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.38 h1:tDUzL85kMvOrvpCt8P64SbGgVFtJB11GPi2AdmITgb4=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.38/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.39 h1:sIwSjlJGOaRJjw44/HXaeTblZMjseqr6OOio1tz/+JI=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.39/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.40 h1:f7+saIsbq4EF86mUqe0uiecQOJYMOdfi5uATADmUG94=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.40/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
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/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 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4=
|
||||||
@@ -106,6 +112,8 @@ github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590 h1:PnxRU8L8Y2q82vFC2Qd
|
|||||||
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590/go.mod h1:XmAOs6UJXpNXRwKk+KY/nv5kL6xXYXyellk+A1pTlko=
|
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590/go.mod h1:XmAOs6UJXpNXRwKk+KY/nv5kL6xXYXyellk+A1pTlko=
|
||||||
github.com/oapi-codegen/runtime v1.3.0 h1:vyK1zc0gDWWXgk2xoQa4+X4RNNc5SL2RbTpJS/4vMYA=
|
github.com/oapi-codegen/runtime v1.3.0 h1:vyK1zc0gDWWXgk2xoQa4+X4RNNc5SL2RbTpJS/4vMYA=
|
||||||
github.com/oapi-codegen/runtime v1.3.0/go.mod h1:kOdeacKy7t40Rclb1je37ZLFboFxh+YLy0zaPCMibPY=
|
github.com/oapi-codegen/runtime v1.3.0/go.mod h1:kOdeacKy7t40Rclb1je37ZLFboFxh+YLy0zaPCMibPY=
|
||||||
|
github.com/oapi-codegen/runtime v1.3.1 h1:RgDY6J4OGQLbRXhG/Xpt3vSVqYpHQS7hN4m85+5xB9g=
|
||||||
|
github.com/oapi-codegen/runtime v1.3.1/go.mod h1:kOdeacKy7t40Rclb1je37ZLFboFxh+YLy0zaPCMibPY=
|
||||||
github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNsSFzQATJI=
|
github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNsSFzQATJI=
|
||||||
github.com/oschwald/geoip2-golang v1.13.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
github.com/oschwald/geoip2-golang v1.13.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
||||||
github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
|
github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
|
||||||
@@ -130,12 +138,12 @@ github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzM
|
|||||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
|
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/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a h1:w3tdWGKbLGBPtR/8/oO74W6hmz0qE5q0z9aqSAewaaM=
|
||||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a/go.mod h1:S8kfXMp+yh77OxPD4fdM6YUknrZpQxLhvxzS4gDHENY=
|
||||||
github.com/schollz/sqlite3dump v1.3.1 h1:QXizJ7XEJ7hggjqjZ3YRtF3+javm8zKtzNByYtEkPRA=
|
github.com/schollz/sqlite3dump v1.3.1 h1:QXizJ7XEJ7hggjqjZ3YRtF3+javm8zKtzNByYtEkPRA=
|
||||||
github.com/schollz/sqlite3dump v1.3.1/go.mod h1:mzSTjZpJH4zAb1FN3iNlhWPbbdyeBpOaTW0hukyMHyI=
|
github.com/schollz/sqlite3dump v1.3.1/go.mod h1:mzSTjZpJH4zAb1FN3iNlhWPbbdyeBpOaTW0hukyMHyI=
|
||||||
github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI=
|
github.com/shirou/gopsutil/v4 v4.25.8 h1:NnAsw9lN7587WHxjJA9ryDnqhJpFH6A+wagYWTOH970=
|
||||||
github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
|
github.com/shirou/gopsutil/v4 v4.25.8/go.mod h1:q9QdMmfAOVIw7a+eF86P7ISEU6ka+NLgkUxlopV4RwI=
|
||||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||||
github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
@@ -153,10 +161,10 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
|||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 h1:xzABM9let0HLLqFypcxvLmlvEciCHL7+Lv+4vwZqecI=
|
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/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569/go.mod h1:2Ly+NIftZN4de9zRmENdYbvPQeaVIYKWpLFStLFEBgI=
|
||||||
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||||
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||||
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
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 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g=
|
||||||
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
|
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
|
||||||
@@ -186,8 +194,8 @@ 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.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.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@@ -266,9 +274,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
|||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/evanphx/json-patch.v5 v5.9.11 h1:OMPeiLomOQwe8+Ku4nwXsdOmrRw2vGUpP3XgLj3ojNw=
|
gopkg.in/evanphx/json-patch.v5 v5.9.11 h1:OMPeiLomOQwe8+Ku4nwXsdOmrRw2vGUpP3XgLj3ojNw=
|
||||||
gopkg.in/evanphx/json-patch.v5 v5.9.11/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk=
|
gopkg.in/evanphx/json-patch.v5 v5.9.11/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|
||||||
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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+5
-13
@@ -51,20 +51,12 @@ func GetWebIndexTemplate() (*template.Template, error) {
|
|||||||
return tmpl, err
|
return tmpl, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed offline-init.mp4
|
//go:embed offline-v2.ts
|
||||||
var offlineInitSegment []byte
|
var offlineVideoSegment []byte
|
||||||
|
|
||||||
// GetOfflineInitSegment will return the fMP4 init segment for the offline clip.
|
// GetOfflineSegment will return the offline video segment data.
|
||||||
func GetOfflineInitSegment() []byte {
|
func GetOfflineSegment() []byte {
|
||||||
return getFileSystemStaticFileOrDefault("offline-init.mp4", offlineInitSegment)
|
return getFileSystemStaticFileOrDefault("offline-v2.ts", offlineVideoSegment)
|
||||||
}
|
|
||||||
|
|
||||||
//go:embed offline-v2.m4s
|
|
||||||
var offlineMediaSegment []byte
|
|
||||||
|
|
||||||
// GetOfflineMediaSegment will return the fMP4 media segment for the offline clip.
|
|
||||||
func GetOfflineMediaSegment() []byte {
|
|
||||||
return getFileSystemStaticFileOrDefault("offline-v2.m4s", offlineMediaSegment)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed img/logo.png
|
//go:embed img/logo.png
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
.VideoJS_player__GD36e{width:100%;position:relative;overflow:hidden}@media(hover:hover)and (pointer:fine){.VideoJS_player__GD36e .vjs-control-bar .vjs-control:hover .vjs-control-text{display:block!important;visibility:visible!important;width:auto!important;height:auto!important;clip:auto!important;margin:0!important;position:absolute;bottom:45px;left:50%;transform:translateX(-50%);white-space:nowrap;z-index:1000;padding:5px 10px;background:hsla(0,0%,8%,.9);color:#fff;border-radius:4px;font-size:12px;line-height:1.2;pointer-events:none}.VideoJS_player__GD36e .vjs-control-bar .vjs-control:hover .vjs-control-text:empty,.VideoJS_player__GD36e .vjs-volume-panel:hover .vjs-mute-control:not(:hover) .vjs-control-text{display:none!important}.VideoJS_player__GD36e .vjs-control:first-child:hover .vjs-control-text{left:0;transform:translateX(6px)}.VideoJS_player__GD36e .vjs-control:first-child:hover .vjs-control-text:after{left:15%}.VideoJS_player__GD36e .vjs-control:last-child:hover .vjs-control-text{left:auto;right:0;transform:translateX(-6px)}.VideoJS_player__GD36e .vjs-control:last-child:hover .vjs-control-text:after{left:auto;right:15%;transform:translateX(50%)}}.VideoPoster_poster__6rnLj{display:flex;justify-content:center;width:100%;height:100%}.VideoPoster_image__8kRcw{background-color:#000}.OwncastPlayer_container__CR5Ry{display:grid;width:100%;justify-items:center;height:var(--player-container-height);aspect-ratio:16/9}@media(width <= 1200px){.OwncastPlayer_container__CR5Ry{height:100%;max-height:var(--player-container-height)}}@media only screen and (width <= 768px){.OwncastPlayer_container__CR5Ry{height:var(--player-container-height);max-height:var(--player-container-height)}}.OwncastPlayer_container__CR5Ry .OwncastPlayer_player__dCDjy,.OwncastPlayer_container__CR5Ry .OwncastPlayer_poster__tbpwE{width:100%;grid-column:1;grid-row:1}
|
||||||
@@ -1 +0,0 @@
|
|||||||
.VideoJS_player__GD36e{width:100%}.VideoPoster_poster__6rnLj{display:flex;justify-content:center;width:100%;height:100%}.VideoPoster_image__8kRcw{background-color:#000}.OwncastPlayer_container__CR5Ry{display:grid;width:100%;justify-items:center;height:var(--player-container-height);aspect-ratio:16/9}@media(width <= 1200px){.OwncastPlayer_container__CR5Ry{height:100%;max-height:var(--player-container-height)}}@media only screen and (width <= 768px){.OwncastPlayer_container__CR5Ry{height:var(--player-container-height);max-height:var(--player-container-height)}}.OwncastPlayer_container__CR5Ry .OwncastPlayer_player__dCDjy,.OwncastPlayer_container__CR5Ry .OwncastPlayer_poster__tbpwE{width:100%;grid-column:1;grid-row:1}
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user