Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb5b9f9be4 | ||
|
|
135628441a | ||
|
|
4aa7204984 | ||
|
|
1af59a0337 | ||
|
|
c4c97fcc8c | ||
|
|
9c46e42792 | ||
|
|
efa803aab6 | ||
|
|
6ea02a2d77 | ||
|
|
631f7d2d5e | ||
|
|
e44a4cb2e1 | ||
|
|
f4b95419a6 | ||
|
|
1a5cf49563 | ||
|
|
efef0b0fee | ||
|
|
ee7b8a71ab | ||
|
|
e7c9a51e96 | ||
|
|
78a954f365 | ||
|
|
355c0b7be9 | ||
|
|
3bcb2d36f9 | ||
|
|
b240de9d4a | ||
|
|
f5001837cb | ||
|
|
6ea916b1f0 | ||
|
|
db6fd22215 | ||
|
|
691842008d | ||
|
|
685f78bef8 | ||
|
|
3ce267863b | ||
|
|
e4231cb57d | ||
|
|
03946b13ca | ||
|
|
f1a81bf086 | ||
|
|
7a88374362 | ||
|
|
663a62431b | ||
|
|
1d4acc284d |
@@ -0,0 +1,211 @@
|
|||||||
|
name: Build Artifacts
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
release_tag:
|
||||||
|
description: 'Release tag'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
release_version:
|
||||||
|
description: 'Release version number (e.g. v0.3.7-alpha)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
info_version:
|
||||||
|
description: 'Informational version number (e.g. 0.3.7-alpha)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
secrets:
|
||||||
|
apple_developer_certificate_p12_base64:
|
||||||
|
required: true
|
||||||
|
apple_developer_certificate_password:
|
||||||
|
required: true
|
||||||
|
ac_username:
|
||||||
|
required: true
|
||||||
|
ac_password:
|
||||||
|
required: true
|
||||||
|
gh_token:
|
||||||
|
required: true
|
||||||
|
jobs:
|
||||||
|
build_and_upload_mac:
|
||||||
|
name: Mac Build & Upload
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
if: contains(github.event.head_commit.message, '[no build]') == false
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: macos-latest
|
||||||
|
kind: macOS
|
||||||
|
target: osx-x64
|
||||||
|
- os: macos-latest
|
||||||
|
kind: macOS
|
||||||
|
target: osx-arm64
|
||||||
|
steps:
|
||||||
|
- name: Get the sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup .NET Core
|
||||||
|
uses: actions/setup-dotnet@v1
|
||||||
|
with:
|
||||||
|
dotnet-version: 6.0.x
|
||||||
|
|
||||||
|
- name: Clean
|
||||||
|
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: dotnet restore -r "${{ matrix.target}}"
|
||||||
|
|
||||||
|
- name: Import Code-Signing Certificates
|
||||||
|
uses: Apple-Actions/import-codesign-certs@v1
|
||||||
|
with:
|
||||||
|
p12-file-base64: ${{ secrets.apple_developer_certificate_p12_base64 }}
|
||||||
|
p12-password: ${{ secrets.apple_developer_certificate_password }}
|
||||||
|
|
||||||
|
- name: Calculate Release Name
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
release_name="ErsatzTV-${{ inputs.release_version }}-${{ matrix.target }}"
|
||||||
|
echo "RELEASE_NAME=${release_name}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
shell: bash
|
||||||
|
run: dotnet publish ErsatzTV/ErsatzTV.csproj --framework net6.0 --runtime "${{ matrix.target }}" -c Release -o publish -p:InformationalVersion="${{ inputs.release_version }}-${{ matrix.target }}" -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded -p:PublishSingleFile=true --self-contained true
|
||||||
|
|
||||||
|
- name: Bundle
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
plutil -replace CFBundleShortVersionString -string "${{ inputs.info_version }}" scripts/macOS/Info.plist
|
||||||
|
plutil -replace CFBundleVersion -string "${{ inputs.info_version }}" scripts/macOS/Info.plist
|
||||||
|
scripts/macOS/bundle.sh
|
||||||
|
|
||||||
|
- name: Sign
|
||||||
|
shell: bash
|
||||||
|
run: scripts/macOS/sign.sh
|
||||||
|
|
||||||
|
- name: Create DMG
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
brew install create-dmg
|
||||||
|
create-dmg \
|
||||||
|
--volname "ErsatzTV" \
|
||||||
|
--volicon "artwork/ErsatzTV.icns" \
|
||||||
|
--window-pos 200 120 \
|
||||||
|
--window-size 800 400 \
|
||||||
|
--icon-size 100 \
|
||||||
|
--icon "ErsatzTV.app" 200 190 \
|
||||||
|
--hide-extension "ErsatzTV.app" \
|
||||||
|
--app-drop-link 600 185 \
|
||||||
|
"ErsatzTV.dmg" \
|
||||||
|
"ErsatzTV.app/"
|
||||||
|
|
||||||
|
- name: Notarize
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
brew tap mitchellh/gon
|
||||||
|
brew install mitchellh/gon/gon
|
||||||
|
gon -log-level=debug -log-json ./gon.json
|
||||||
|
env:
|
||||||
|
AC_USERNAME: ${{ secrets.ac_username }}
|
||||||
|
AC_PASSWORD: ${{ secrets.ac_password }}
|
||||||
|
|
||||||
|
- name: Cleanup
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mv ErsatzTV.dmg "${{ env.RELEASE_NAME }}.dmg"
|
||||||
|
rm -r publish
|
||||||
|
rm -r ErsatzTV.app
|
||||||
|
|
||||||
|
- name: Delete old release assets
|
||||||
|
uses: mknejp/delete-release-assets@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.gh_token }}
|
||||||
|
tag: ${{ inputs.release_tag }}
|
||||||
|
fail-if-no-assets: false
|
||||||
|
assets: |
|
||||||
|
*${{ matrix.target }}.dmg
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
prerelease: true
|
||||||
|
tag_name: ${{ inputs.release_tag }}
|
||||||
|
files: |
|
||||||
|
${{ env.RELEASE_NAME }}.dmg
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.gh_token }}
|
||||||
|
build_and_upload:
|
||||||
|
name: Build & Upload
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
if: contains(github.event.head_commit.message, '[no build]') == false
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-latest
|
||||||
|
kind: linux
|
||||||
|
target: linux-x64
|
||||||
|
- os: ubuntu-latest
|
||||||
|
kind: linux
|
||||||
|
target: linux-arm
|
||||||
|
- os: windows-latest
|
||||||
|
kind: windows
|
||||||
|
target: win-x64
|
||||||
|
steps:
|
||||||
|
- name: Get the sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup .NET Core
|
||||||
|
uses: actions/setup-dotnet@v1
|
||||||
|
with:
|
||||||
|
dotnet-version: 6.0.x
|
||||||
|
|
||||||
|
- name: Clean
|
||||||
|
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: dotnet restore -r "${{ matrix.target}}"
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# Define some variables for things we need
|
||||||
|
release_name="ErsatzTV-${{ inputs.release_version }}-${{ matrix.target }}"
|
||||||
|
echo "RELEASE_NAME=${release_name}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Build everything
|
||||||
|
dotnet publish ErsatzTV/ErsatzTV.csproj --framework net6.0 --runtime "${{ matrix.target }}" -c Release -o "$release_name" -p:InformationalVersion="${{ inputs.release_version }}-${{ matrix.target }}" -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded -p:PublishSingleFile=true --self-contained true
|
||||||
|
|
||||||
|
# Pack files
|
||||||
|
if [ "${{ matrix.kind }}" == "windows" ]; then
|
||||||
|
7z a -tzip "${release_name}.zip" "./${release_name}/*"
|
||||||
|
else
|
||||||
|
tar czvf "${release_name}.tar.gz" "$release_name"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Delete output directory
|
||||||
|
rm -r "$release_name"
|
||||||
|
env:
|
||||||
|
AC_USERNAME: ${{ secrets.ac_username }}
|
||||||
|
AC_PASSWORD: ${{ secrets.ac_password }}
|
||||||
|
|
||||||
|
- name: Delete old release assets
|
||||||
|
uses: mknejp/delete-release-assets@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.gh_token }}
|
||||||
|
tag: ${{ inputs.release_tag }}
|
||||||
|
fail-if-no-assets: false
|
||||||
|
assets: |
|
||||||
|
*${{ matrix.target }}.zip
|
||||||
|
*${{ matrix.target }}.tar.gz
|
||||||
|
- name: Publish
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
prerelease: true
|
||||||
|
tag_name: ${{ inputs.release_tag }}
|
||||||
|
files: |
|
||||||
|
${{ env.RELEASE_NAME }}.zip
|
||||||
|
${{ env.RELEASE_NAME }}.tar.gz
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.gh_token }}
|
||||||
+39
-91
@@ -1,49 +1,19 @@
|
|||||||
name: Build
|
name: Build
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
jobs:
|
jobs:
|
||||||
build_and_test:
|
calculate_version:
|
||||||
runs-on: ${{ matrix.os }}
|
name: Calculate version information
|
||||||
strategy:
|
runs-on: ubuntu-latest
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ windows-latest, ubuntu-latest, macos-latest ]
|
|
||||||
steps:
|
steps:
|
||||||
- name: Get the sources
|
- name: Get the sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup .NET Core
|
|
||||||
uses: actions/setup-dotnet@v1
|
|
||||||
with:
|
|
||||||
dotnet-version: 6.0.x
|
|
||||||
|
|
||||||
- name: Clean
|
|
||||||
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: dotnet restore
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: dotnet build --configuration Release --no-restore
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: dotnet test --no-restore --verbosity normal
|
|
||||||
build_and_push:
|
|
||||||
name: Build & Publish to Docker Hub
|
|
||||||
needs: build_and_test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event_name == 'push' && !contains(github.event.head_commit.message, '[no docker]')
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
- name: Extract Docker Tag
|
||||||
- name: Extract Git Tag
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
tag=$(git describe --tags --abbrev=0)
|
tag=$(git describe --tags --abbrev=0)
|
||||||
@@ -51,60 +21,38 @@ jobs:
|
|||||||
short=$(git rev-parse --short HEAD)
|
short=$(git rev-parse --short HEAD)
|
||||||
final="${tag2/alpha/$short}"
|
final="${tag2/alpha/$short}"
|
||||||
echo "GIT_TAG=${final}" >> $GITHUB_ENV
|
echo "GIT_TAG=${final}" >> $GITHUB_ENV
|
||||||
|
- name: Extract Artifacts Version
|
||||||
- name: Set up Docker Buildx Base
|
shell: bash
|
||||||
uses: docker/setup-buildx-action@v1
|
run: |
|
||||||
id: builder-base
|
tag=$(git describe --tags --abbrev=0)
|
||||||
|
short=$(git rev-parse --short HEAD)
|
||||||
- name: Set up Docker Buildx NVIDIA
|
final="${tag/alpha/$short}"
|
||||||
uses: docker/setup-buildx-action@v1
|
echo "ARTIFACTS_VERSION=${final}" >> $GITHUB_ENV
|
||||||
id: builder-nvidia
|
echo "INFO_VERSION=${tag:1}" >> $GITHUB_ENV
|
||||||
|
outputs:
|
||||||
- name: Set up Docker Buildx VAAPI
|
git_tag: ${{ env.GIT_TAG }}
|
||||||
uses: docker/setup-buildx-action@v1
|
artifacts_version: ${{ env.ARTIFACTS_VERSION }}
|
||||||
id: builder-vaapi
|
info_version: ${{ env.INFO_VERSION }}
|
||||||
|
build_and_upload:
|
||||||
- name: Login to DockerHub
|
uses: jasongdove/ersatztv/.github/workflows/artifacts.yml@main
|
||||||
uses: docker/login-action@v1
|
needs: calculate_version
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
release_tag: develop
|
||||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
release_version: ${{ needs.calculate_version.outputs.artifacts_version }}
|
||||||
|
info_version: ${{ needs.calculate_version.outputs.info_version }}
|
||||||
- name: Build and push base
|
secrets:
|
||||||
uses: docker/build-push-action@v2
|
apple_developer_certificate_p12_base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }}
|
||||||
with:
|
apple_developer_certificate_password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }}
|
||||||
builder: ${{ steps.builder-base.outputs.name }}
|
ac_username: ${{ secrets.AC_USERNAME }}
|
||||||
context: .
|
ac_password: ${{ secrets.AC_PASSWORD }}
|
||||||
file: ./docker/Dockerfile
|
gh_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
push: true
|
build_and_push:
|
||||||
build-args: |
|
uses: jasongdove/ersatztv/.github/workflows/docker.yml@main
|
||||||
INFO_VERSION=${{ env.GIT_TAG }}-docker
|
needs: calculate_version
|
||||||
tags: |
|
with:
|
||||||
jasongdove/ersatztv:develop
|
base_version: develop
|
||||||
jasongdove/ersatztv:${{ github.sha }}
|
info_version: ${{ needs.calculate_version.outputs.git_tag }}
|
||||||
|
tag_version: ${{ github.sha }}
|
||||||
- name: Build and push nvidia
|
secrets:
|
||||||
uses: docker/build-push-action@v2
|
docker_hub_username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
with:
|
docker_hub_access_token: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||||
builder: ${{ steps.builder-nvidia.outputs.name }}
|
|
||||||
context: .
|
|
||||||
file: ./docker/nvidia/Dockerfile
|
|
||||||
push: true
|
|
||||||
build-args: |
|
|
||||||
INFO_VERSION=${{ env.GIT_TAG }}-docker-nvidia
|
|
||||||
tags: |
|
|
||||||
jasongdove/ersatztv:develop-nvidia
|
|
||||||
jasongdove/ersatztv:${{ github.sha }}-nvidia
|
|
||||||
|
|
||||||
- name: Build and push vaapi
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
builder: ${{ steps.builder-vaapi.outputs.name }}
|
|
||||||
context: .
|
|
||||||
file: ./docker/vaapi/Dockerfile
|
|
||||||
push: true
|
|
||||||
build-args: |
|
|
||||||
INFO_VERSION=${{ env.GIT_TAG }}-docker-vaapi
|
|
||||||
tags: |
|
|
||||||
jasongdove/ersatztv:develop-vaapi
|
|
||||||
jasongdove/ersatztv:${{ github.sha }}-vaapi
|
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
name: Build & Publish to Docker Hub
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
base_version:
|
||||||
|
description: 'Base version (latest or develop)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
info_version:
|
||||||
|
description: 'Informational version number (e.g. 0.3.7-alpha)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
tag_version:
|
||||||
|
description: 'Docker tag version (e.g. v0.3.7)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
secrets:
|
||||||
|
docker_hub_username:
|
||||||
|
required: true
|
||||||
|
docker_hub_access_token:
|
||||||
|
required: true
|
||||||
|
jobs:
|
||||||
|
build_and_push:
|
||||||
|
name: Build & Publish
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: contains(github.event.head_commit.message, '[no build]') == false
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx Base
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
id: builder-base
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx NVIDIA
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
id: builder-nvidia
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx VAAPI
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
id: builder-vaapi
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.docker_hub_username }}
|
||||||
|
password: ${{ secrets.docker_hub_access_token }}
|
||||||
|
|
||||||
|
- name: Build and push base
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
builder: ${{ steps.builder-base.outputs.name }}
|
||||||
|
context: .
|
||||||
|
file: ./docker/Dockerfile
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
INFO_VERSION=${{ inputs.info_version }}-docker
|
||||||
|
tags: |
|
||||||
|
jasongdove/ersatztv:${{ inputs.base_version }}
|
||||||
|
jasongdove/ersatztv:${{ inputs.tag_version }}
|
||||||
|
|
||||||
|
- name: Build and push nvidia
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
builder: ${{ steps.builder-nvidia.outputs.name }}
|
||||||
|
context: .
|
||||||
|
file: ./docker/nvidia/Dockerfile
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
INFO_VERSION=${{ inputs.info_version }}-docker-nvidia
|
||||||
|
tags: |
|
||||||
|
jasongdove/ersatztv:${{ inputs.base_version }}-nvidia
|
||||||
|
jasongdove/ersatztv:${{ inputs.tag_version }}-nvidia
|
||||||
|
|
||||||
|
- name: Build and push vaapi
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
builder: ${{ steps.builder-vaapi.outputs.name }}
|
||||||
|
context: .
|
||||||
|
file: ./docker/vaapi/Dockerfile
|
||||||
|
push: true
|
||||||
|
build-args: |
|
||||||
|
INFO_VERSION=${{ inputs.info_version }}-docker-vaapi
|
||||||
|
tags: |
|
||||||
|
jasongdove/ersatztv:${{ inputs.base_version }}-vaapi
|
||||||
|
jasongdove/ersatztv:${{ inputs.tag_version }}-vaapi
|
||||||
@@ -3,7 +3,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Deploy docs
|
name: Deploy docs
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
name: Pull Request
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
jobs:
|
||||||
|
build_and_test:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ windows-latest, ubuntu-latest, macos-latest ]
|
||||||
|
steps:
|
||||||
|
- name: Get the sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup .NET Core
|
||||||
|
uses: actions/setup-dotnet@v1
|
||||||
|
with:
|
||||||
|
dotnet-version: 6.0.x
|
||||||
|
|
||||||
|
- name: Clean
|
||||||
|
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: dotnet restore
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: dotnet build --configuration Release --no-restore
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: dotnet test --no-restore --verbosity normal
|
||||||
+39
-128
@@ -1,142 +1,53 @@
|
|||||||
name: Publish
|
name: Release
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [ published ]
|
types: [ published ]
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
calculate_version:
|
||||||
name: Release
|
name: Calculate version information
|
||||||
strategy:
|
runs-on: ubuntu-latest
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
kind: linux
|
|
||||||
target: linux-x64
|
|
||||||
- os: ubuntu-latest
|
|
||||||
kind: linux
|
|
||||||
target: linux-arm
|
|
||||||
- os: windows-latest
|
|
||||||
kind: windows
|
|
||||||
target: win-x64
|
|
||||||
- os: macos-latest
|
|
||||||
kind: macOS
|
|
||||||
target: osx-x64
|
|
||||||
- os: macos-latest
|
|
||||||
kind: macOS
|
|
||||||
target: osx-arm64
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Get the sources
|
- name: Get the sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup .NET Core
|
|
||||||
uses: actions/setup-dotnet@v1
|
|
||||||
with:
|
|
||||||
dotnet-version: 6.0.x
|
|
||||||
|
|
||||||
- name: Clean
|
|
||||||
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: dotnet restore
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
# Define some variables for things we need
|
|
||||||
tag=$(git describe --tags --abbrev=0)
|
|
||||||
release_name="ErsatzTV-$tag-${{ matrix.target }}"
|
|
||||||
|
|
||||||
# Build everything
|
|
||||||
dotnet publish ErsatzTV/ErsatzTV.csproj --framework net6.0 --runtime "${{ matrix.target }}" -c Release -o "$release_name" /property:InformationalVersion="${tag:1}-${{ matrix.target }}" /property:EnableCompressionInSingleFile=true /property:DebugType=Embedded /property:PublishSingleFile=true --self-contained true
|
|
||||||
|
|
||||||
# Pack files
|
|
||||||
if [ "${{ matrix.target }}" == "win-x64" ]; then
|
|
||||||
7z a -tzip "${release_name}.zip" "./${release_name}/*"
|
|
||||||
else
|
|
||||||
tar czvf "${release_name}.tar.gz" "$release_name"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Delete output directory
|
|
||||||
rm -r "$release_name"
|
|
||||||
|
|
||||||
- name: Publish
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
with:
|
|
||||||
prerelease: true
|
|
||||||
files: |
|
|
||||||
ErsatzTV*.zip
|
|
||||||
ErsatzTV*.tar.gz
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
build_and_push:
|
|
||||||
name: Build & Publish to Docker Hub
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
- name: Extract Docker Tag
|
||||||
- name: Extract Git Tag
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
tag=$(git describe --tags --abbrev=0)
|
tag=$(git describe --tags --abbrev=0)
|
||||||
echo "GIT_TAG=${tag:1}" >> $GITHUB_ENV
|
echo "GIT_TAG=${tag:1}" >> $GITHUB_ENV
|
||||||
echo "DOCKER_TAG=${tag/-alpha/}" >> $GITHUB_ENV
|
echo "DOCKER_TAG=${tag/-alpha/}" >> $GITHUB_ENV
|
||||||
|
- name: Extract Artifacts Version
|
||||||
- name: Set up Docker Buildx Base
|
shell: bash
|
||||||
uses: docker/setup-buildx-action@v1
|
run: |
|
||||||
id: builder-base
|
tag=$(git describe --tags --abbrev=0)
|
||||||
|
echo "ARTIFACTS_VERSION=${tag}" >> $GITHUB_ENV
|
||||||
- name: Set up Docker Buildx NVIDIA
|
echo "INFO_VERSION=${tag:1}" >> $GITHUB_ENV
|
||||||
uses: docker/setup-buildx-action@v1
|
outputs:
|
||||||
id: builder-nvidia
|
git_tag: ${{ env.GIT_TAG }}
|
||||||
|
docker_tag: ${{ env.DOCKER_TAG }}
|
||||||
- name: Set up Docker Buildx VAAPI
|
artifacts_version: ${{ env.ARTIFACTS_VERSION }}
|
||||||
uses: docker/setup-buildx-action@v1
|
info_version: ${{ env.INFO_VERSION }}
|
||||||
id: builder-vaapi
|
build_and_upload:
|
||||||
|
uses: jasongdove/ersatztv/.github/workflows/artifacts.yml@main
|
||||||
- name: Login to DockerHub
|
needs: calculate_version
|
||||||
uses: docker/login-action@v1
|
with:
|
||||||
with:
|
release_tag: ${{ needs.calculate_version.outputs.artifacts_version }}
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
release_version: ${{ needs.calculate_version.outputs.artifacts_version }}
|
||||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
info_version: ${{ needs.calculate_version.outputs.info_version }}
|
||||||
|
secrets:
|
||||||
- name: Build and push base
|
apple_developer_certificate_p12_base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }}
|
||||||
uses: docker/build-push-action@v2
|
apple_developer_certificate_password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }}
|
||||||
with:
|
ac_username: ${{ secrets.AC_USERNAME }}
|
||||||
builder: ${{ steps.builder-base.outputs.name }}
|
ac_password: ${{ secrets.AC_PASSWORD }}
|
||||||
context: .
|
gh_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
file: ./docker/Dockerfile
|
build_and_push:
|
||||||
push: true
|
uses: jasongdove/ersatztv/.github/workflows/docker.yml@main
|
||||||
build-args: |
|
needs: calculate_version
|
||||||
INFO_VERSION=${{ env.GIT_TAG }}-docker
|
with:
|
||||||
tags: |
|
base_version: latest
|
||||||
jasongdove/ersatztv:latest
|
info_version: ${{ needs.calculate_version.outputs.git_tag }}
|
||||||
jasongdove/ersatztv:${{ env.DOCKER_TAG }}
|
tag_version: ${{ needs.calculate_version.outputs.docker_tag }}
|
||||||
|
secrets:
|
||||||
- name: Build and push nvidia
|
docker_hub_username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
uses: docker/build-push-action@v2
|
docker_hub_access_token: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||||
with:
|
|
||||||
builder: ${{ steps.builder-nvidia.outputs.name }}
|
|
||||||
context: .
|
|
||||||
file: ./docker/nvidia/Dockerfile
|
|
||||||
push: true
|
|
||||||
build-args: |
|
|
||||||
INFO_VERSION=${{ env.GIT_TAG }}-docker-nvidia
|
|
||||||
tags: |
|
|
||||||
jasongdove/ersatztv:latest-nvidia
|
|
||||||
jasongdove/ersatztv:${{ env.DOCKER_TAG }}-nvidia
|
|
||||||
|
|
||||||
- name: Build and push vaapi
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
builder: ${{ steps.builder-vaapi.outputs.name }}
|
|
||||||
context: .
|
|
||||||
file: ./docker/vaapi/Dockerfile
|
|
||||||
push: true
|
|
||||||
build-args: |
|
|
||||||
INFO_VERSION=${{ env.GIT_TAG }}-docker-vaapi
|
|
||||||
tags: |
|
|
||||||
jasongdove/ersatztv:latest-vaapi
|
|
||||||
jasongdove/ersatztv:${{ env.DOCKER_TAG }}-vaapi
|
|
||||||
|
|||||||
+19
-2
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
## [0.3.8-alpha] - 2022-01-23
|
||||||
|
### Fixed
|
||||||
|
- Fix issue preventing some versions of ffmpeg (usually 4.4.x) from streaming MPEG-TS (Legacy) channels at all
|
||||||
|
- The issue appears to be caused by using a thread count other than `1`
|
||||||
|
- Thread count is now forced to `1` for all streaming modes other than HLS Segmenter
|
||||||
|
- Fix bug with HLS Segmenter in cultures where `.` is a group/thousands separator
|
||||||
|
- Fix search results page crashing with some media kinds
|
||||||
|
- Always use MPEG-TS or MPEG-TS (Legacy) streaming mode with HDHR (Plex)
|
||||||
|
- Other configured modes will fall back to MPEG-TS when accessed by Plex
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Upgrade ffmpeg from 4.4 to 5.0 in all docker images
|
||||||
|
- Upgrading from 4.4 to 5.0 is recommended for all installations
|
||||||
|
|
||||||
|
## [0.3.7-alpha] - 2022-01-17
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fix local folder scanners to properly detect removed/re-added folders with unchanged contents
|
- Fix local folder scanners to properly detect removed/re-added folders with unchanged contents
|
||||||
- Fix double-click startup on mac
|
- Fix double-click startup on mac
|
||||||
@@ -909,8 +924,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- Initial release to facilitate testing outside of Docker.
|
- Initial release to facilitate testing outside of Docker.
|
||||||
|
|
||||||
|
|
||||||
[Unreleased]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.6-alpha...HEAD
|
[Unreleased]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.8-alpha...HEAD
|
||||||
[0.3.5-alpha]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.5-alpha...v0.3.6-alpha
|
[0.3.8-alpha]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.7-alpha...v0.3.8-alpha
|
||||||
|
[0.3.7-alpha]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.6-alpha...v0.3.7-alpha
|
||||||
|
[0.3.6-alpha]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.5-alpha...v0.3.6-alpha
|
||||||
[0.3.5-alpha]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.4-alpha...v0.3.5-alpha
|
[0.3.5-alpha]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.4-alpha...v0.3.5-alpha
|
||||||
[0.3.4-alpha]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.3-alpha...v0.3.4-alpha
|
[0.3.4-alpha]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.3-alpha...v0.3.4-alpha
|
||||||
[0.3.3-alpha]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.2-alpha...v0.3.3-alpha
|
[0.3.3-alpha]: https://github.com/jasongdove/ErsatzTV/compare/v0.3.2-alpha...v0.3.3-alpha
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
public CalculateSettings() => _calculator = new FFmpegPlaybackSettingsCalculator();
|
public CalculateSettings() => _calculator = new FFmpegPlaybackSettingsCalculator();
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Should_UseSpecifiedThreadCount_ForTransportStream()
|
public void Should_Not_UseSpecifiedThreadCount_ForTransportStream()
|
||||||
{
|
{
|
||||||
|
// MPEG-TS requires realtime output which is hardcoded to a single thread
|
||||||
|
|
||||||
FFmpegProfile ffmpegProfile = TestProfile() with { ThreadCount = 7 };
|
FFmpegProfile ffmpegProfile = TestProfile() with { ThreadCount = 7 };
|
||||||
|
|
||||||
FFmpegPlaybackSettings actual = _calculator.CalculateSettings(
|
FFmpegPlaybackSettings actual = _calculator.CalculateSettings(
|
||||||
@@ -31,18 +33,19 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ThreadCount.Should().Be(7);
|
actual.ThreadCount.Should().Be(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Should_UseSpecifiedThreadCount_ForHttpLiveStreaming()
|
public void Should_UseSpecifiedThreadCount_ForHttpLiveStreamingSegmenter()
|
||||||
{
|
{
|
||||||
FFmpegProfile ffmpegProfile = TestProfile() with { ThreadCount = 7 };
|
FFmpegProfile ffmpegProfile = TestProfile() with { ThreadCount = 7 };
|
||||||
|
|
||||||
FFmpegPlaybackSettings actual = _calculator.CalculateSettings(
|
FFmpegPlaybackSettings actual = _calculator.CalculateSettings(
|
||||||
StreamingMode.HttpLiveStreamingDirect,
|
StreamingMode.HttpLiveStreamingSegmenter,
|
||||||
ffmpegProfile,
|
ffmpegProfile,
|
||||||
new MediaVersion(),
|
new MediaVersion(),
|
||||||
new MediaStream(),
|
new MediaStream(),
|
||||||
@@ -50,7 +53,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ThreadCount.Should().Be(7);
|
actual.ThreadCount.Should().Be(7);
|
||||||
}
|
}
|
||||||
@@ -69,7 +73,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
string[] expected = { "+genpts", "+discardcorrupt", "+igndts" };
|
string[] expected = { "+genpts", "+discardcorrupt", "+igndts" };
|
||||||
actual.FormatFlags.Count.Should().Be(expected.Length);
|
actual.FormatFlags.Count.Should().Be(expected.Length);
|
||||||
@@ -90,7 +95,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
string[] expected = { "+genpts", "+discardcorrupt", "+igndts" };
|
string[] expected = { "+genpts", "+discardcorrupt", "+igndts" };
|
||||||
actual.FormatFlags.Count.Should().Be(expected.Length);
|
actual.FormatFlags.Count.Should().Be(expected.Length);
|
||||||
@@ -111,7 +117,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.RealtimeOutput.Should().BeTrue();
|
actual.RealtimeOutput.Should().BeTrue();
|
||||||
}
|
}
|
||||||
@@ -130,7 +137,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.RealtimeOutput.Should().BeTrue();
|
actual.RealtimeOutput.Should().BeTrue();
|
||||||
}
|
}
|
||||||
@@ -151,7 +159,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
now,
|
now,
|
||||||
now.AddMinutes(5),
|
now.AddMinutes(5),
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.StreamSeek.IsSome.Should().BeTrue();
|
actual.StreamSeek.IsSome.Should().BeTrue();
|
||||||
actual.StreamSeek.IfNone(TimeSpan.Zero).Should().Be(TimeSpan.FromMinutes(5));
|
actual.StreamSeek.IfNone(TimeSpan.Zero).Should().Be(TimeSpan.FromMinutes(5));
|
||||||
@@ -173,7 +182,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
now,
|
now,
|
||||||
now.AddMinutes(5),
|
now.AddMinutes(5),
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.StreamSeek.IsSome.Should().BeTrue();
|
actual.StreamSeek.IsSome.Should().BeTrue();
|
||||||
actual.StreamSeek.IfNone(TimeSpan.Zero).Should().Be(TimeSpan.FromMinutes(5));
|
actual.StreamSeek.IfNone(TimeSpan.Zero).Should().Be(TimeSpan.FromMinutes(5));
|
||||||
@@ -193,7 +203,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
}
|
}
|
||||||
@@ -219,7 +230,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
}
|
}
|
||||||
@@ -245,7 +257,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
}
|
}
|
||||||
@@ -271,7 +284,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeFalse();
|
actual.PadToDesiredResolution.Should().BeFalse();
|
||||||
@@ -299,7 +313,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeTrue();
|
actual.PadToDesiredResolution.Should().BeTrue();
|
||||||
@@ -326,7 +341,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
IDisplaySize scaledSize = actual.ScaledSize.IfNone(new MediaVersion { Width = 0, Height = 0 });
|
IDisplaySize scaledSize = actual.ScaledSize.IfNone(new MediaVersion { Width = 0, Height = 0 });
|
||||||
scaledSize.Width.Should().Be(1280);
|
scaledSize.Width.Should().Be(1280);
|
||||||
@@ -356,7 +372,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeFalse();
|
actual.PadToDesiredResolution.Should().BeFalse();
|
||||||
@@ -384,7 +401,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeFalse();
|
actual.PadToDesiredResolution.Should().BeFalse();
|
||||||
@@ -413,7 +431,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeTrue();
|
actual.PadToDesiredResolution.Should().BeTrue();
|
||||||
@@ -445,7 +464,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeFalse();
|
actual.PadToDesiredResolution.Should().BeFalse();
|
||||||
@@ -477,7 +497,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeFalse();
|
actual.PadToDesiredResolution.Should().BeFalse();
|
||||||
@@ -508,7 +529,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeFalse();
|
actual.PadToDesiredResolution.Should().BeFalse();
|
||||||
@@ -540,7 +562,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeFalse();
|
actual.PadToDesiredResolution.Should().BeFalse();
|
||||||
@@ -574,7 +597,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeFalse();
|
actual.PadToDesiredResolution.Should().BeFalse();
|
||||||
@@ -606,7 +630,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeTrue();
|
actual.PadToDesiredResolution.Should().BeTrue();
|
||||||
@@ -637,7 +662,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeFalse();
|
actual.PadToDesiredResolution.Should().BeFalse();
|
||||||
@@ -667,7 +693,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeTrue();
|
actual.PadToDesiredResolution.Should().BeTrue();
|
||||||
@@ -699,7 +726,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.ScaledSize.IsNone.Should().BeTrue();
|
actual.ScaledSize.IsNone.Should().BeTrue();
|
||||||
actual.PadToDesiredResolution.Should().BeFalse();
|
actual.PadToDesiredResolution.Should().BeFalse();
|
||||||
@@ -727,7 +755,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.AudioCodec.Should().Be("aac");
|
actual.AudioCodec.Should().Be("aac");
|
||||||
}
|
}
|
||||||
@@ -752,7 +781,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.AudioCodec.Should().Be("copy");
|
actual.AudioCodec.Should().Be("copy");
|
||||||
}
|
}
|
||||||
@@ -778,7 +808,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.AudioCodec.Should().Be("aac");
|
actual.AudioCodec.Should().Be("aac");
|
||||||
}
|
}
|
||||||
@@ -804,7 +835,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.AudioCodec.Should().Be("copy");
|
actual.AudioCodec.Should().Be("copy");
|
||||||
}
|
}
|
||||||
@@ -831,7 +863,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.AudioBitrate.IfNone(0).Should().Be(2424);
|
actual.AudioBitrate.IfNone(0).Should().Be(2424);
|
||||||
}
|
}
|
||||||
@@ -858,7 +891,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.AudioBufferSize.IfNone(0).Should().Be(2424);
|
actual.AudioBufferSize.IfNone(0).Should().Be(2424);
|
||||||
}
|
}
|
||||||
@@ -885,7 +919,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.AudioChannels.IfNone(0).Should().Be(6);
|
actual.AudioChannels.IfNone(0).Should().Be(6);
|
||||||
}
|
}
|
||||||
@@ -912,7 +947,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.AudioSampleRate.IfNone(0).Should().Be(48);
|
actual.AudioSampleRate.IfNone(0).Should().Be(48);
|
||||||
}
|
}
|
||||||
@@ -938,7 +974,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.AudioChannels.IfNone(0).Should().Be(6);
|
actual.AudioChannels.IfNone(0).Should().Be(6);
|
||||||
}
|
}
|
||||||
@@ -964,7 +1001,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.AudioSampleRate.IfNone(0).Should().Be(48);
|
actual.AudioSampleRate.IfNone(0).Should().Be(48);
|
||||||
}
|
}
|
||||||
@@ -991,7 +1029,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.FromMinutes(2));
|
TimeSpan.FromMinutes(2),
|
||||||
|
false);
|
||||||
|
|
||||||
actual.AudioDuration.IfNone(TimeSpan.MinValue).Should().Be(TimeSpan.FromMinutes(2));
|
actual.AudioDuration.IfNone(TimeSpan.MinValue).Should().Be(TimeSpan.FromMinutes(2));
|
||||||
}
|
}
|
||||||
@@ -1017,7 +1056,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.NormalizeLoudness.Should().BeTrue();
|
actual.NormalizeLoudness.Should().BeTrue();
|
||||||
}
|
}
|
||||||
@@ -1043,7 +1083,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.NormalizeLoudness.Should().BeFalse();
|
actual.NormalizeLoudness.Should().BeFalse();
|
||||||
}
|
}
|
||||||
@@ -1071,7 +1112,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
|
|||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
DateTimeOffset.Now,
|
DateTimeOffset.Now,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
actual.HardwareAcceleration.Should().Be(HardwareAccelerationKind.Qsv);
|
actual.HardwareAcceleration.Should().Be(HardwareAccelerationKind.Qsv);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ namespace ErsatzTV.Core.Tests.Scheduling
|
|||||||
playoutItems[2].FillerKind.Should().Be(FillerKind.None);
|
playoutItems[2].FillerKind.Should().Be(FillerKind.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Should_Fill_Exactly_To_Next_Schedule_Item_Flood()
|
public void Should_Fill_Exactly_To_Next_Schedule_Item_Flood()
|
||||||
{
|
{
|
||||||
Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromHours(1));
|
Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromHours(1));
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
public List<string> FormatFlags { get; set; }
|
public List<string> FormatFlags { get; set; }
|
||||||
public HardwareAccelerationKind HardwareAcceleration { get; set; }
|
public HardwareAccelerationKind HardwareAcceleration { get; set; }
|
||||||
public string VideoDecoder { get; set; }
|
public string VideoDecoder { get; set; }
|
||||||
public bool RealtimeOutput => true;
|
public bool RealtimeOutput { get; set; }
|
||||||
public Option<TimeSpan> StreamSeek { get; set; }
|
public Option<TimeSpan> StreamSeek { get; set; }
|
||||||
public Option<IDisplaySize> ScaledSize { get; set; }
|
public Option<IDisplaySize> ScaledSize { get; set; }
|
||||||
public bool PadToDesiredResolution { get; set; }
|
public bool PadToDesiredResolution { get; set; }
|
||||||
|
|||||||
@@ -51,14 +51,22 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
DateTimeOffset start,
|
DateTimeOffset start,
|
||||||
DateTimeOffset now,
|
DateTimeOffset now,
|
||||||
TimeSpan inPoint,
|
TimeSpan inPoint,
|
||||||
TimeSpan outPoint)
|
TimeSpan outPoint,
|
||||||
|
bool hlsRealtime)
|
||||||
{
|
{
|
||||||
var result = new FFmpegPlaybackSettings
|
var result = new FFmpegPlaybackSettings
|
||||||
{
|
{
|
||||||
ThreadCount = ffmpegProfile.ThreadCount,
|
FormatFlags = CommonFormatFlags,
|
||||||
FormatFlags = CommonFormatFlags
|
RealtimeOutput = streamingMode switch
|
||||||
|
{
|
||||||
|
StreamingMode.HttpLiveStreamingSegmenter => hlsRealtime,
|
||||||
|
_ => true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// always use one thread with realtime output
|
||||||
|
result.ThreadCount = result.RealtimeOutput ? 1 : ffmpegProfile.ThreadCount;
|
||||||
|
|
||||||
if (now != start || inPoint != TimeSpan.Zero)
|
if (now != start || inPoint != TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
result.StreamSeek = now - start + inPoint;
|
result.StreamSeek = now - start + inPoint;
|
||||||
|
|||||||
@@ -379,7 +379,14 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
_arguments.Add($"{format}");
|
_arguments.Add($"{format}");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FFmpegProcessBuilder WithInitialDiscontinuity()
|
||||||
|
{
|
||||||
|
_arguments.Add("-mpegts_flags");
|
||||||
|
_arguments.Add("+initial_discontinuity");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public FFmpegProcessBuilder WithHls(string channelNumber, Option<MediaVersion> mediaVersion)
|
public FFmpegProcessBuilder WithHls(string channelNumber, Option<MediaVersion> mediaVersion)
|
||||||
{
|
{
|
||||||
const int SEGMENT_SECONDS = 4;
|
const int SEGMENT_SECONDS = 4;
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
start,
|
start,
|
||||||
now,
|
now,
|
||||||
inPoint,
|
inPoint,
|
||||||
outPoint);
|
outPoint,
|
||||||
|
hlsRealtime);
|
||||||
|
|
||||||
Option<WatermarkOptions> watermarkOptions =
|
Option<WatermarkOptions> watermarkOptions =
|
||||||
await GetWatermarkOptions(channel, globalWatermark, videoVersion, None, None);
|
await GetWatermarkOptions(channel, globalWatermark, videoVersion, None, None);
|
||||||
@@ -158,10 +159,10 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
// HLS needs to segment and generate playlist
|
// HLS needs to segment and generate playlist
|
||||||
case StreamingMode.HttpLiveStreamingSegmenter:
|
case StreamingMode.HttpLiveStreamingSegmenter:
|
||||||
return builder.WithHls(channel.Number, videoVersion)
|
return builder.WithHls(channel.Number, videoVersion)
|
||||||
.WithRealtimeOutput(hlsRealtime)
|
|
||||||
.Build();
|
.Build();
|
||||||
default:
|
default:
|
||||||
return builder.WithFormat("mpegts")
|
return builder.WithFormat("mpegts")
|
||||||
|
.WithInitialDiscontinuity()
|
||||||
.WithPipe()
|
.WithPipe()
|
||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
@@ -221,7 +222,6 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
// HLS needs to segment and generate playlist
|
// HLS needs to segment and generate playlist
|
||||||
case StreamingMode.HttpLiveStreamingSegmenter:
|
case StreamingMode.HttpLiveStreamingSegmenter:
|
||||||
return builder.WithHls(channel.Number, None)
|
return builder.WithHls(channel.Number, None)
|
||||||
.WithRealtimeOutput(hlsRealtime)
|
|
||||||
.Build();
|
.Build();
|
||||||
default:
|
default:
|
||||||
return builder.WithFormat("mpegts")
|
return builder.WithFormat("mpegts")
|
||||||
@@ -255,7 +255,7 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
.WithThreads(1)
|
.WithThreads(1)
|
||||||
.WithQuiet()
|
.WithQuiet()
|
||||||
.WithFormatFlags(playbackSettings.FormatFlags)
|
.WithFormatFlags(playbackSettings.FormatFlags)
|
||||||
.WithRealtimeOutput(playbackSettings.RealtimeOutput)
|
.WithRealtimeOutput(true)
|
||||||
.WithInput($"http://localhost:{Settings.ListenPort}/iptv/channel/{channel.Number}.m3u8?mode=segmenter")
|
.WithInput($"http://localhost:{Settings.ListenPort}/iptv/channel/{channel.Number}.m3u8?mode=segmenter")
|
||||||
.WithMap("0")
|
.WithMap("0")
|
||||||
.WithCopyCodec()
|
.WithCopyCodec()
|
||||||
@@ -335,7 +335,8 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
DateTimeOffset.UnixEpoch,
|
DateTimeOffset.UnixEpoch,
|
||||||
DateTimeOffset.UnixEpoch,
|
DateTimeOffset.UnixEpoch,
|
||||||
TimeSpan.Zero,
|
TimeSpan.Zero,
|
||||||
TimeSpan.Zero);
|
TimeSpan.Zero,
|
||||||
|
false);
|
||||||
|
|
||||||
FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath, false, _logger)
|
FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath, false, _logger)
|
||||||
.WithThreads(1)
|
.WithThreads(1)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace ErsatzTV.Core.FFmpeg
|
namespace ErsatzTV.Core.FFmpeg
|
||||||
@@ -55,7 +56,11 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var duration = TimeSpan.FromSeconds(double.Parse(lines[i].TrimEnd(',').Split(':')[1]));
|
var duration = TimeSpan.FromSeconds(
|
||||||
|
double.Parse(
|
||||||
|
lines[i].TrimEnd(',').Split(':')[1],
|
||||||
|
NumberStyles.Number,
|
||||||
|
CultureInfo.InvariantCulture));
|
||||||
if (currentTime < filterBefore)
|
if (currentTime < filterBefore)
|
||||||
{
|
{
|
||||||
currentTime += duration;
|
currentTime += duration;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace ErsatzTV.Core.Hdhr
|
|||||||
|
|
||||||
public string URL => _channel.StreamingMode switch
|
public string URL => _channel.StreamingMode switch
|
||||||
{
|
{
|
||||||
StreamingMode.HttpLiveStreamingDirect => $"{_scheme}://{_host}/iptv/channel/{_channel.Number}.m3u8",
|
StreamingMode.TransportStream => $"{_scheme}://{_host}/iptv/channel/{_channel.Number}.ts?mode=ts-legacy",
|
||||||
_ => $"{_scheme}://{_host}/iptv/channel/{_channel.Number}.ts"
|
_ => $"{_scheme}://{_host}/iptv/channel/{_channel.Number}.ts"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ namespace ErsatzTV.Core.Iptv
|
|||||||
StreamingMode.HttpLiveStreamingDirect => "m3u8?mode=hls-direct",
|
StreamingMode.HttpLiveStreamingDirect => "m3u8?mode=hls-direct",
|
||||||
StreamingMode.HttpLiveStreamingSegmenter => "m3u8?mode=segmenter",
|
StreamingMode.HttpLiveStreamingSegmenter => "m3u8?mode=segmenter",
|
||||||
StreamingMode.TransportStreamHybrid => "ts",
|
StreamingMode.TransportStreamHybrid => "ts",
|
||||||
_ => "ts?mode=legacy"
|
_ => "ts?mode=ts-legacy"
|
||||||
};
|
};
|
||||||
|
|
||||||
string vcodec = channel.FFmpegProfile.VideoCodec.Split("_").Head();
|
string vcodec = channel.FFmpegProfile.VideoCodec.Split("_").Head();
|
||||||
|
|||||||
@@ -72,10 +72,10 @@ namespace ErsatzTV.Core.Scheduling
|
|||||||
itemDuration,
|
itemDuration,
|
||||||
itemChapters);
|
itemChapters);
|
||||||
|
|
||||||
// if the current time is before the next schedule item, but the current finish
|
// if the next schedule item is supposed to start during this item,
|
||||||
// is after, we need to move on to the next schedule item
|
// don't schedule this item and just move on
|
||||||
willFinishInTime = itemStartTime > peekScheduleItemStart ||
|
willFinishInTime = peekScheduleItemStart < itemStartTime ||
|
||||||
itemEndTimeWithFiller <= peekScheduleItemStart;
|
peekScheduleItemStart >= itemEndTimeWithFiller;
|
||||||
|
|
||||||
if (willFinishInTime)
|
if (willFinishInTime)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -153,8 +153,10 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
|||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Filter(mm => ids.Contains(mm.MovieId))
|
.Filter(mm => ids.Contains(mm.MovieId))
|
||||||
.Include(mm => mm.Artwork)
|
.Include(mm => mm.Artwork)
|
||||||
.OrderBy(mm => mm.SortTitle)
|
|
||||||
.Include(mm => mm.Movie)
|
.Include(mm => mm.Movie)
|
||||||
|
.ThenInclude(m => m.MediaVersions)
|
||||||
|
.ThenInclude(mv => mv.MediaFiles)
|
||||||
|
.OrderBy(mm => mm.SortTitle)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,9 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
|||||||
.Include(mvm => mvm.MusicVideo)
|
.Include(mvm => mvm.MusicVideo)
|
||||||
.ThenInclude(mv => mv.Artist)
|
.ThenInclude(mv => mv.Artist)
|
||||||
.ThenInclude(a => a.ArtistMetadata)
|
.ThenInclude(a => a.ArtistMetadata)
|
||||||
|
.Include(mvm => mvm.MusicVideo)
|
||||||
|
.ThenInclude(e => e.MediaVersions)
|
||||||
|
.ThenInclude(mv => mv.MediaFiles)
|
||||||
.Include(mvm => mvm.Artwork)
|
.Include(mvm => mvm.Artwork)
|
||||||
.OrderBy(mvm => mvm.SortTitle)
|
.OrderBy(mvm => mvm.SortTitle)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
|||||||
.Filter(ovm => ids.Contains(ovm.OtherVideoId))
|
.Filter(ovm => ids.Contains(ovm.OtherVideoId))
|
||||||
.Include(ovm => ovm.OtherVideo)
|
.Include(ovm => ovm.OtherVideo)
|
||||||
.Include(ovm => ovm.Artwork)
|
.Include(ovm => ovm.Artwork)
|
||||||
|
.Include(ovm => ovm.OtherVideo)
|
||||||
|
.ThenInclude(ov => ov.MediaVersions)
|
||||||
|
.ThenInclude(mv => mv.MediaFiles)
|
||||||
.OrderBy(ovm => ovm.SortTitle)
|
.OrderBy(ovm => ovm.SortTitle)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
|||||||
.Filter(ovm => ids.Contains(ovm.SongId))
|
.Filter(ovm => ids.Contains(ovm.SongId))
|
||||||
.Include(ovm => ovm.Song)
|
.Include(ovm => ovm.Song)
|
||||||
.Include(ovm => ovm.Artwork)
|
.Include(ovm => ovm.Artwork)
|
||||||
|
.Include(sm => sm.Song)
|
||||||
|
.ThenInclude(s => s.MediaVersions)
|
||||||
|
.ThenInclude(mv => mv.MediaFiles)
|
||||||
.OrderBy(ovm => ovm.SortTitle)
|
.OrderBy(ovm => ovm.SortTitle)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace ErsatzTV.Infrastructure.Health.Checks
|
|||||||
{
|
{
|
||||||
public class FFmpegVersionHealthCheck : BaseHealthCheck, IFFmpegVersionHealthCheck
|
public class FFmpegVersionHealthCheck : BaseHealthCheck, IFFmpegVersionHealthCheck
|
||||||
{
|
{
|
||||||
private const string BundledVersion = "N-105153-g8abc192236";
|
private const string BundledVersion = "N-105324-g0f5fd44dc9";
|
||||||
private readonly IConfigElementRepository _configElementRepository;
|
private readonly IConfigElementRepository _configElementRepository;
|
||||||
|
|
||||||
public FFmpegVersionHealthCheck(IConfigElementRepository configElementRepository)
|
public FFmpegVersionHealthCheck(IConfigElementRepository configElementRepository)
|
||||||
@@ -71,19 +71,15 @@ namespace ErsatzTV.Infrastructure.Health.Checks
|
|||||||
|
|
||||||
private Option<HealthCheckResult> ValidateVersion(string version, string app)
|
private Option<HealthCheckResult> ValidateVersion(string version, string app)
|
||||||
{
|
{
|
||||||
if (version.StartsWith("3."))
|
if (version.StartsWith("3.") || version.StartsWith("4."))
|
||||||
{
|
{
|
||||||
return FailResult($"{app} version {version} is too old; please install 4.4!");
|
return FailResult($"{app} version {version} is too old; please install 5.0!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version.StartsWith("4.3"))
|
if (!version.StartsWith("5.0") && version != BundledVersion)
|
||||||
{
|
{
|
||||||
return WarningResult($"{app} version 4.4 is now supported, please upgrade from 4.3!");
|
return WarningResult(
|
||||||
}
|
$"{app} version {version} is unexpected and may have problems; please install 5.0!");
|
||||||
|
|
||||||
if (!version.StartsWith("4.4") && version != BundledVersion)
|
|
||||||
{
|
|
||||||
return WarningResult($"{app} version {version} is unexpected and may have problems; please install 4.4!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return None;
|
return None;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace ErsatzTV.Controllers
|
|||||||
{
|
{
|
||||||
FFmpegProcessRequest request = mode switch
|
FFmpegProcessRequest request = mode switch
|
||||||
{
|
{
|
||||||
"legacy" => new GetConcatProcessByChannelNumber(Request.Scheme, Request.Host.ToString(), channelNumber),
|
"ts-legacy" => new GetConcatProcessByChannelNumber(Request.Scheme, Request.Host.ToString(), channelNumber),
|
||||||
_ => new GetWrappedProcessByChannelNumber(Request.Scheme, Request.Host.ToString(), channelNumber)
|
_ => new GetWrappedProcessByChannelNumber(Request.Scheme, Request.Host.ToString(), channelNumber)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -59,4 +59,11 @@
|
|||||||
<EmbeddedResource Include="Resources\ISO-639-2_utf-8.txt" />
|
<EmbeddedResource Include="Resources\ISO-639-2_utf-8.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="..\artwork\Ersatztv.icns">
|
||||||
|
<Link>Ersatztv.icns</Link>
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
+26
-9
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ErsatzTV.Core;
|
using ErsatzTV.Core;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
@@ -12,14 +11,32 @@ namespace ErsatzTV
|
|||||||
{
|
{
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
private static IConfiguration Configuration { get; } = new ConfigurationBuilder()
|
static Program()
|
||||||
.SetBasePath(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location))
|
{
|
||||||
.AddJsonFile("appsettings.json", false, true)
|
var executablePath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
|
||||||
.AddJsonFile(
|
var executable = Path.GetFileNameWithoutExtension(executablePath);
|
||||||
$"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json",
|
|
||||||
true)
|
IConfigurationBuilder builder = new ConfigurationBuilder();
|
||||||
.AddEnvironmentVariables()
|
|
||||||
.Build();
|
if ("dotnet".Equals(executable, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
builder = builder.SetBasePath(Path.GetDirectoryName(typeof(Program).Assembly.Location));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder = builder.SetBasePath(Path.GetDirectoryName(executablePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
Configuration = builder
|
||||||
|
.AddJsonFile("appsettings.json", false, true)
|
||||||
|
.AddJsonFile(
|
||||||
|
$"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json",
|
||||||
|
true)
|
||||||
|
.AddEnvironmentVariables()
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IConfiguration Configuration { get; }
|
||||||
|
|
||||||
public static async Task<int> Main(string[] args)
|
public static async Task<int> Main(string[] args)
|
||||||
{
|
{
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Executable
+17
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
IFS='.' read -ra ADDR <<< "$1"
|
||||||
|
ICONSET=${ADDR[0]}.iconset
|
||||||
|
mkdir $ICONSET
|
||||||
|
sips -z 16 16 $1 --out $ICONSET/icon_16x16.png
|
||||||
|
sips -z 32 32 $1 --out $ICONSET/icon_16x16@2x.png
|
||||||
|
sips -z 32 32 $1 --out $ICONSET/icon_32x32.png
|
||||||
|
sips -z 64 64 $1 --out $ICONSET/icon_32x32@2x.png
|
||||||
|
sips -z 128 128 $1 --out $ICONSET/icon_128x128.png
|
||||||
|
sips -z 256 256 $1 --out $ICONSET/icon_128x128@2x.png
|
||||||
|
sips -z 256 256 $1 --out $ICONSET/icon_256x256.png
|
||||||
|
sips -z 512 512 $1 --out $ICONSET/icon_256x256@2x.png
|
||||||
|
sips -z 512 512 $1 --out $ICONSET/icon_512x512.png
|
||||||
|
cp $1 $ICONSET/icon_512x512@2x.png
|
||||||
|
iconutil -c icns $ICONSET
|
||||||
|
rm -R $ICONSET
|
||||||
|
|
||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0-focal-amd64 AS dotnet-runtime
|
FROM mcr.microsoft.com/dotnet/aspnet:6.0-focal-amd64 AS dotnet-runtime
|
||||||
|
|
||||||
FROM jasongdove/ffmpeg:4.4-ubuntu2004 AS runtime-base
|
FROM jasongdove/ffmpeg:5.0-ubuntu2004 AS runtime-base
|
||||||
COPY --from=dotnet-runtime /usr/share/dotnet /usr/share/dotnet
|
COPY --from=dotnet-runtime /usr/share/dotnet /usr/share/dotnet
|
||||||
RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y libicu-dev tzdata
|
RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y libicu-dev tzdata
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ WORKDIR /source
|
|||||||
COPY *.sln .
|
COPY *.sln .
|
||||||
COPY nuget.config .
|
COPY nuget.config .
|
||||||
COPY lib/nuget/* ./lib/nuget/
|
COPY lib/nuget/* ./lib/nuget/
|
||||||
|
COPY artwork/* ./artwork/
|
||||||
COPY ErsatzTV/*.csproj ./ErsatzTV/
|
COPY ErsatzTV/*.csproj ./ErsatzTV/
|
||||||
COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/
|
COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/
|
||||||
COPY ErsatzTV.Core/*.csproj ./ErsatzTV.Core/
|
COPY ErsatzTV.Core/*.csproj ./ErsatzTV.Core/
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ WORKDIR /source
|
|||||||
COPY *.sln .
|
COPY *.sln .
|
||||||
COPY nuget.config .
|
COPY nuget.config .
|
||||||
COPY lib/nuget/* ./lib/nuget/
|
COPY lib/nuget/* ./lib/nuget/
|
||||||
|
COPY artwork/* ./artwork/
|
||||||
COPY ErsatzTV/*.csproj ./ErsatzTV/
|
COPY ErsatzTV/*.csproj ./ErsatzTV/
|
||||||
COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/
|
COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/
|
||||||
COPY ErsatzTV.Core/*.csproj ./ErsatzTV.Core/
|
COPY ErsatzTV.Core/*.csproj ./ErsatzTV.Core/
|
||||||
@@ -25,7 +26,7 @@ ARG INFO_VERSION="unknown"
|
|||||||
RUN dotnet publish -c release -o /app -r linux-x64 --self-contained false --no-restore /p:DebugType=Embedded /p:InformationalVersion=${INFO_VERSION}
|
RUN dotnet publish -c release -o /app -r linux-x64 --self-contained false --no-restore /p:DebugType=Embedded /p:InformationalVersion=${INFO_VERSION}
|
||||||
|
|
||||||
# final stage/image
|
# final stage/image
|
||||||
FROM jasongdove/ffmpeg:4.4-nvidia2004 AS runtime-base
|
FROM jasongdove/ffmpeg:5.0-nvidia2004 AS runtime-base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 8409
|
EXPOSE 8409
|
||||||
COPY --from=build /app ./
|
COPY --from=build /app ./
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0-focal-amd64 AS dotnet-runtime
|
FROM mcr.microsoft.com/dotnet/aspnet:6.0-focal-amd64 AS dotnet-runtime
|
||||||
|
|
||||||
FROM jasongdove/ffmpeg-base:4.4-nvidia2004 AS runtime-base
|
FROM jasongdove/ffmpeg-base:5.0-nvidia2004 AS runtime-base
|
||||||
COPY --from=dotnet-runtime /usr/share/dotnet /usr/share/dotnet
|
COPY --from=dotnet-runtime /usr/share/dotnet /usr/share/dotnet
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& DEBIAN_FRONTEND="noninteractive" apt-get install -y libicu-dev tzdata \
|
&& DEBIAN_FRONTEND="noninteractive" apt-get install -y libicu-dev tzdata \
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ WORKDIR /source
|
|||||||
COPY *.sln .
|
COPY *.sln .
|
||||||
COPY nuget.config .
|
COPY nuget.config .
|
||||||
COPY lib/nuget/* ./lib/nuget/
|
COPY lib/nuget/* ./lib/nuget/
|
||||||
|
COPY artwork/* ./artwork/
|
||||||
COPY ErsatzTV/*.csproj ./ErsatzTV/
|
COPY ErsatzTV/*.csproj ./ErsatzTV/
|
||||||
COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/
|
COPY ErsatzTV.Application/*.csproj ./ErsatzTV.Application/
|
||||||
COPY ErsatzTV.Core/*.csproj ./ErsatzTV.Core/
|
COPY ErsatzTV.Core/*.csproj ./ErsatzTV.Core/
|
||||||
@@ -25,7 +26,7 @@ ARG INFO_VERSION="unknown"
|
|||||||
RUN dotnet publish -c release -o /app -r linux-x64 --self-contained false --no-restore /p:DebugType=Embedded /p:InformationalVersion=${INFO_VERSION}
|
RUN dotnet publish -c release -o /app -r linux-x64 --self-contained false --no-restore /p:DebugType=Embedded /p:InformationalVersion=${INFO_VERSION}
|
||||||
|
|
||||||
# final stage/image
|
# final stage/image
|
||||||
FROM jasongdove/ffmpeg:4.4-vaapi2004 AS runtime-base
|
FROM jasongdove/ffmpeg:5.0-vaapi2004 AS runtime-base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 8409
|
EXPOSE 8409
|
||||||
COPY --from=build /app ./
|
COPY --from=build /app ./
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0-focal-amd64 AS dotnet-runtime
|
FROM mcr.microsoft.com/dotnet/aspnet:6.0-focal-amd64 AS dotnet-runtime
|
||||||
|
|
||||||
FROM jasongdove/ffmpeg-base:4.4-vaapi2004 AS runtime-base
|
FROM jasongdove/ffmpeg-base:5.0-vaapi2004 AS runtime-base
|
||||||
COPY --from=dotnet-runtime /usr/share/dotnet /usr/share/dotnet
|
COPY --from=dotnet-runtime /usr/share/dotnet /usr/share/dotnet
|
||||||
RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y libicu-dev \
|
RUN apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y libicu-dev \
|
||||||
tzdata \
|
tzdata \
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
ErsatzTV needs to know about your media items in order to create channels.
|
ErsatzTV needs to know about your media items in order to create channels.
|
||||||
Four library kinds are currently supported: [Local](#local-libraries), [Plex](#plex-libraries), [Jellyfin](#jellyfin-libraries) and [Emby](#emby-libraries).
|
Four library kinds are currently supported: [Local](#local-libraries), [Plex](#plex-libraries), [Jellyfin](#jellyfin-libraries) and [Emby](#emby-libraries).
|
||||||
|
|
||||||
|
ErsatzTV supports a subset of media kinds when synchronizing from media servers, i.e. [Plex](#plex-libraries), [Jellyfin](#jellyfin-libraries) and [Emby](#emby-libraries) only support `Movies` and `Shows`.
|
||||||
|
[Local Libraries](#local-libraries) support all media kinds: `Movies`, `Shows`, `Music Videos`, `Other Videos` and `Songs`.
|
||||||
|
|
||||||
## Local Libraries
|
## Local Libraries
|
||||||
|
|
||||||
Local libraries provide a way to schedule media directly from folders; no media server is necessary.
|
Local libraries provide a way to schedule media directly from folders; no media server is necessary.
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ docker run -d \
|
|||||||
|
|
||||||
7. Open your browser to `http://[docker-ip]:8409` (First run may take a few minutes to be available.)
|
7. Open your browser to `http://[docker-ip]:8409` (First run may take a few minutes to be available.)
|
||||||
|
|
||||||
|
## Development Builds
|
||||||
|
|
||||||
|
Development builds for all supported architectures can be found on the [develop release](https://github.com/jasongdove/ErsatzTV/releases/tag/develop).
|
||||||
|
As with the docker development tags, the development builds are updated with every push to the main branch and have the potential to be less stable than releases.
|
||||||
|
|
||||||
## Windows
|
## Windows
|
||||||
|
|
||||||
### Manual Installation
|
### Manual Installation
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"notarize": [{
|
||||||
|
"path": "./ErsatzTV.dmg",
|
||||||
|
"bundle_id": "com.jasongdove.ersatztv",
|
||||||
|
"staple": true
|
||||||
|
}],
|
||||||
|
|
||||||
|
"apple_id": {
|
||||||
|
"password": "@env:AC_PASSWORD"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.automation.apple-events</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>ErsatzTV</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>launcher.sh</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>ErsatzTV.icns</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.jasongdove.ersatztv</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>ErsatzTV</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>0.3.7-alpha</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>0.3.7-alpha</string>
|
||||||
|
<key>NSHighResolutionCapable</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string>NSApplication</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
Executable
+26
@@ -0,0 +1,26 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
SCRIPT_FOLDER=$(dirname ${BASH_SOURCE[0]})
|
||||||
|
REPO_ROOT="$SCRIPT_FOLDER/../.."
|
||||||
|
|
||||||
|
APP_NAME="$REPO_ROOT/ErsatzTV.app"
|
||||||
|
PUBLISH_OUTPUT_DIRECTORY="$REPO_ROOT/publish/."
|
||||||
|
INFO_PLIST="$SCRIPT_FOLDER/Info.plist"
|
||||||
|
ICON_SOURCE="$REPO_ROOT/artwork/ErsatzTV.icns"
|
||||||
|
ICON_FILE="ErsatzTV.icns"
|
||||||
|
|
||||||
|
if [ -d "$APP_NAME" ]
|
||||||
|
then
|
||||||
|
rm -rf "$APP_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir "$APP_NAME"
|
||||||
|
|
||||||
|
mkdir "$APP_NAME/Contents"
|
||||||
|
mkdir "$APP_NAME/Contents/MacOS"
|
||||||
|
mkdir "$APP_NAME/Contents/Resources"
|
||||||
|
|
||||||
|
cp "$INFO_PLIST" "$APP_NAME/Contents/Info.plist"
|
||||||
|
cp "$ICON_SOURCE" "$APP_NAME/Contents/Resources/$ICON_FILE"
|
||||||
|
cp -a "$PUBLISH_OUTPUT_DIRECTORY" "$APP_NAME/Contents/MacOS"
|
||||||
|
cp "$SCRIPT_FOLDER/launcher.sh" "$APP_NAME/Contents/MacOS/"
|
||||||
Executable
+9
@@ -0,0 +1,9 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
DIR=$(cd "$(dirname "$0")"; pwd)
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
echo "running from $DIR"
|
||||||
|
|
||||||
|
open -a Terminal $DIR/ErsatzTV
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
SCRIPT_FOLDER=$(dirname ${BASH_SOURCE[0]})
|
||||||
|
REPO_ROOT="$SCRIPT_FOLDER/../.."
|
||||||
|
|
||||||
|
DMG_NAME="$REPO_ROOT/ErsatzTV.dmg"
|
||||||
|
ENTITLEMENTS="$SCRIPT_FOLDER/ErsatzTV.entitlements"
|
||||||
|
SIGNING_IDENTITY="C3BBCFB2D6851FF0DCA6CAC06A3EF1ECE71F9FFF"
|
||||||
|
|
||||||
|
codesign --force --timestamp --options=runtime --entitlements "$ENTITLEMENTS" --sign "$SIGNING_IDENTITY" "$DMG_NAME"
|
||||||
Executable
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
SCRIPT_FOLDER=$(dirname ${BASH_SOURCE[0]})
|
||||||
|
REPO_ROOT="$SCRIPT_FOLDER/../.."
|
||||||
|
|
||||||
|
APP_NAME="$REPO_ROOT/ErsatzTV.app"
|
||||||
|
ENTITLEMENTS="$SCRIPT_FOLDER/ErsatzTV.entitlements"
|
||||||
|
SIGNING_IDENTITY="C3BBCFB2D6851FF0DCA6CAC06A3EF1ECE71F9FFF"
|
||||||
|
|
||||||
|
find "$APP_NAME/Contents/MacOS/"|while read fname; do
|
||||||
|
if [[ -f $fname ]]; then
|
||||||
|
echo "[INFO] Signing $fname"
|
||||||
|
codesign --force --timestamp --options=runtime --entitlements "$ENTITLEMENTS" --sign "$SIGNING_IDENTITY" "$fname"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "[INFO] Signing app file"
|
||||||
|
|
||||||
|
codesign --force --timestamp --options=runtime --entitlements "$ENTITLEMENTS" --sign "$SIGNING_IDENTITY" "$APP_NAME"
|
||||||
Reference in New Issue
Block a user