mirror of
https://github.com/immich-app/immich.git
synced 2026-07-05 20:27:52 -07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2383986d2d |
@@ -51,10 +51,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
@@ -79,10 +80,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
permission-pull-requests: write
|
||||
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
@@ -202,10 +205,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Select Xcode 26
|
||||
run: sudo xcode-select -s /Applications/Xcode_26.2.app/Contents/Developer
|
||||
|
||||
@@ -19,10 +19,12 @@ jobs:
|
||||
actions: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
permission-actions: write
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
@@ -31,10 +31,11 @@ jobs:
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -63,10 +64,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
@@ -44,10 +44,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
@@ -23,10 +23,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
|
||||
@@ -21,10 +21,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
@@ -54,10 +55,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
@@ -20,10 +20,12 @@ jobs:
|
||||
artifact: ${{ steps.get-artifact.outputs.result }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-actions: read
|
||||
permission-pull-requests: read
|
||||
|
||||
- if: ${{ github.event.workflow_run.conclusion != 'success' }}
|
||||
run: echo 'The triggering workflow did not succeed' && exit 1
|
||||
@@ -126,10 +128,13 @@ jobs:
|
||||
if: ${{ fromJson(needs.checks.outputs.artifact).found && fromJson(needs.checks.outputs.parameters).shouldDeploy }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
permission-actions: read
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
@@ -17,10 +17,12 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
@@ -15,10 +15,12 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: write
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
@@ -35,6 +35,8 @@ jobs:
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: write
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Find translation PR
|
||||
id: find_pr
|
||||
|
||||
@@ -14,10 +14,11 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Require PR to have a changelog label
|
||||
uses: mheap/github-action-required-labels@0ac283b4e65c1fb28ce6079dea5546ceca98ccbe # v5.5.2
|
||||
|
||||
@@ -12,10 +12,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
permission-pull-requests: write
|
||||
|
||||
- uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0
|
||||
with:
|
||||
|
||||
@@ -54,10 +54,11 @@ jobs:
|
||||
permissions: {} # No job-level permissions are needed because it uses the app-token
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: write
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -135,6 +136,8 @@ jobs:
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: write
|
||||
permission-actions: read
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
@@ -14,10 +14,11 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-pull-requests: write
|
||||
|
||||
- uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||
with:
|
||||
@@ -32,10 +33,11 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-pull-requests: write
|
||||
|
||||
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
|
||||
@@ -16,10 +16,11 @@ jobs:
|
||||
packages: write
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
@@ -20,10 +20,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
@@ -49,10 +50,11 @@ jobs:
|
||||
working-directory: ./mobile
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
+36
-18
@@ -17,10 +17,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
@@ -77,10 +78,11 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -105,10 +107,11 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -136,10 +139,11 @@ jobs:
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -167,10 +171,11 @@ jobs:
|
||||
working-directory: ./packages/cli
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -211,10 +216,11 @@ jobs:
|
||||
working-directory: ./web
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -249,10 +255,11 @@ jobs:
|
||||
working-directory: ./web
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -277,10 +284,11 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -327,10 +335,11 @@ jobs:
|
||||
working-directory: ./e2e
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -359,10 +368,11 @@ jobs:
|
||||
working-directory: ./server
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -395,10 +405,11 @@ jobs:
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -472,10 +483,11 @@ jobs:
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -580,10 +592,11 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
@@ -618,10 +631,11 @@ jobs:
|
||||
working-directory: ./machine-learning
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
@@ -648,10 +662,11 @@ jobs:
|
||||
working-directory: ./.github
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -678,10 +693,11 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
@@ -699,10 +715,11 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
@@ -757,10 +774,11 @@ jobs:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
@@ -24,10 +24,11 @@ jobs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-contents: read
|
||||
|
||||
- name: Check what should run
|
||||
id: check
|
||||
@@ -47,10 +48,11 @@ jobs:
|
||||
if: ${{ fromJSON(needs.pre-job.outputs.should_run).i18n == true }}
|
||||
steps:
|
||||
- id: token
|
||||
uses: immich-app/devtools/actions/create-workflow-token@9db058b2e6eec20e07760b0e17a0505c78ec3191 # create-workflow-token-action-v2.0.1
|
||||
uses: immich-app/devtools/actions/create-workflow-token@1af396ae134e4bc3b63d947e672bc68bf4ff9dc5 # create-workflow-token-action-v3.0.0
|
||||
with:
|
||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||
permission-pull-requests: read
|
||||
|
||||
- name: Bot review status
|
||||
env:
|
||||
|
||||
@@ -431,10 +431,6 @@
|
||||
"transcoding_realtime_description": "Allows transcoding to be performed in real-time as the video is being streamed. Enables quality switching, but may cause higher playback latency and stuttering depending on server capabilities.",
|
||||
"transcoding_realtime_enabled": "Enable real-time transcoding",
|
||||
"transcoding_realtime_enabled_description": "If disabled, the server will refuse to start new real-time transcoding sessions.",
|
||||
"transcoding_realtime_resolutions": "Resolutions",
|
||||
"transcoding_realtime_resolutions_description": "The resolutions offered for real-time transcoding. A variant is only offered when its resolution is no larger than the source. Higher resolutions may cause playback issues if the server cannot transcode them quickly enough.",
|
||||
"transcoding_realtime_video_codecs": "Video codecs",
|
||||
"transcoding_realtime_video_codecs_description": "The video codecs offered for real-time transcoding. Clients will choose the best option they support during playback. AV1 is more efficient than HEVC, which is more efficient than H.264. When using hardware acceleration, only select the codecs the accelerator can encode. When using software transcoding, note that H.264 is faster than AV1, which is faster than HEVC.",
|
||||
"transcoding_reference_frames": "Reference frames",
|
||||
"transcoding_reference_frames_description": "The number of frames to reference when compressing a given frame. Higher values improve compression efficiency, but slow down encoding. 0 sets this value automatically.",
|
||||
"transcoding_required_description": "Only videos not in an accepted format",
|
||||
|
||||
Generated
-1
@@ -452,7 +452,6 @@ Class | Method | HTTP request | Description
|
||||
- [FacialRecognitionConfig](doc//FacialRecognitionConfig.md)
|
||||
- [FoldersResponse](doc//FoldersResponse.md)
|
||||
- [FoldersUpdate](doc//FoldersUpdate.md)
|
||||
- [HlsVideoResolution](doc//HlsVideoResolution.md)
|
||||
- [ImageFormat](doc//ImageFormat.md)
|
||||
- [IntegrityReport](doc//IntegrityReport.md)
|
||||
- [IntegrityReportResponseDto](doc//IntegrityReportResponseDto.md)
|
||||
|
||||
Generated
-1
@@ -173,7 +173,6 @@ part 'model/face_dto.dart';
|
||||
part 'model/facial_recognition_config.dart';
|
||||
part 'model/folders_response.dart';
|
||||
part 'model/folders_update.dart';
|
||||
part 'model/hls_video_resolution.dart';
|
||||
part 'model/image_format.dart';
|
||||
part 'model/integrity_report.dart';
|
||||
part 'model/integrity_report_response_dto.dart';
|
||||
|
||||
Generated
-2
@@ -391,8 +391,6 @@ class ApiClient {
|
||||
return FoldersResponse.fromJson(value);
|
||||
case 'FoldersUpdate':
|
||||
return FoldersUpdate.fromJson(value);
|
||||
case 'HlsVideoResolution':
|
||||
return HlsVideoResolutionTypeTransformer().decode(value);
|
||||
case 'ImageFormat':
|
||||
return ImageFormatTypeTransformer().decode(value);
|
||||
case 'IntegrityReport':
|
||||
|
||||
Generated
-3
@@ -106,9 +106,6 @@ String parameterToString(dynamic value) {
|
||||
if (value is Colorspace) {
|
||||
return ColorspaceTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is HlsVideoResolution) {
|
||||
return HlsVideoResolutionTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is ImageFormat) {
|
||||
return ImageFormatTypeTransformer().encode(value).toString();
|
||||
}
|
||||
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
/// HLS video resolution
|
||||
class HlsVideoResolution {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const HlsVideoResolution._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final int value;
|
||||
|
||||
@override
|
||||
String toString() => value.toString();
|
||||
|
||||
int toJson() => value;
|
||||
|
||||
static const number480 = HlsVideoResolution._(480);
|
||||
static const number720 = HlsVideoResolution._(720);
|
||||
static const number1080 = HlsVideoResolution._(1080);
|
||||
static const number1440 = HlsVideoResolution._(1440);
|
||||
static const number2160 = HlsVideoResolution._(2160);
|
||||
|
||||
/// List of all possible values in this [enum][HlsVideoResolution].
|
||||
static const values = <HlsVideoResolution>[
|
||||
number480,
|
||||
number720,
|
||||
number1080,
|
||||
number1440,
|
||||
number2160,
|
||||
];
|
||||
|
||||
static HlsVideoResolution? fromJson(dynamic value) => HlsVideoResolutionTypeTransformer().decode(value);
|
||||
|
||||
static List<HlsVideoResolution> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <HlsVideoResolution>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = HlsVideoResolution.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [HlsVideoResolution] to int,
|
||||
/// and [decode] dynamic data back to [HlsVideoResolution].
|
||||
class HlsVideoResolutionTypeTransformer {
|
||||
factory HlsVideoResolutionTypeTransformer() => _instance ??= const HlsVideoResolutionTypeTransformer._();
|
||||
|
||||
const HlsVideoResolutionTypeTransformer._();
|
||||
|
||||
int encode(HlsVideoResolution data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a HlsVideoResolution.
|
||||
///
|
||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||
///
|
||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||
/// and users are still using an old app with the old code.
|
||||
HlsVideoResolution? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case 480: return HlsVideoResolution.number480;
|
||||
case 720: return HlsVideoResolution.number720;
|
||||
case 1080: return HlsVideoResolution.number1080;
|
||||
case 1440: return HlsVideoResolution.number1440;
|
||||
case 2160: return HlsVideoResolution.number2160;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [HlsVideoResolutionTypeTransformer] instance.
|
||||
static HlsVideoResolutionTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
@@ -14,40 +14,26 @@ class SystemConfigFFmpegRealtimeDto {
|
||||
/// Returns a new [SystemConfigFFmpegRealtimeDto] instance.
|
||||
SystemConfigFFmpegRealtimeDto({
|
||||
required this.enabled,
|
||||
this.resolutions = const [],
|
||||
this.videoCodecs = const [],
|
||||
});
|
||||
|
||||
/// Enable real-time HLS transcoding (alpha)
|
||||
bool enabled;
|
||||
|
||||
/// Resolutions to use for real-time HLS transcoding
|
||||
List<HlsVideoResolution> resolutions;
|
||||
|
||||
/// Video codecs to use for real-time HLS transcoding
|
||||
List<VideoCodec> videoCodecs;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SystemConfigFFmpegRealtimeDto &&
|
||||
other.enabled == enabled &&
|
||||
_deepEquality.equals(other.resolutions, resolutions) &&
|
||||
_deepEquality.equals(other.videoCodecs, videoCodecs);
|
||||
other.enabled == enabled;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(enabled.hashCode) +
|
||||
(resolutions.hashCode) +
|
||||
(videoCodecs.hashCode);
|
||||
(enabled.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigFFmpegRealtimeDto[enabled=$enabled, resolutions=$resolutions, videoCodecs=$videoCodecs]';
|
||||
String toString() => 'SystemConfigFFmpegRealtimeDto[enabled=$enabled]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'enabled'] = this.enabled;
|
||||
json[r'resolutions'] = this.resolutions;
|
||||
json[r'videoCodecs'] = this.videoCodecs;
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -61,8 +47,6 @@ class SystemConfigFFmpegRealtimeDto {
|
||||
|
||||
return SystemConfigFFmpegRealtimeDto(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
resolutions: HlsVideoResolution.listFromJson(json[r'resolutions']),
|
||||
videoCodecs: VideoCodec.listFromJson(json[r'videoCodecs']),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@@ -111,8 +95,6 @@ class SystemConfigFFmpegRealtimeDto {
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'enabled',
|
||||
'resolutions',
|
||||
'videoCodecs',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -19120,17 +19120,6 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"HlsVideoResolution": {
|
||||
"description": "HLS video resolution",
|
||||
"enum": [
|
||||
480,
|
||||
720,
|
||||
1080,
|
||||
1440,
|
||||
2160
|
||||
],
|
||||
"type": "integer"
|
||||
},
|
||||
"ImageFormat": {
|
||||
"description": "Image format",
|
||||
"enum": [
|
||||
@@ -25765,26 +25754,10 @@
|
||||
"enabled": {
|
||||
"description": "Enable real-time HLS transcoding (alpha)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"resolutions": {
|
||||
"description": "Resolutions to use for real-time HLS transcoding",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/HlsVideoResolution"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"videoCodecs": {
|
||||
"description": "Video codecs to use for real-time HLS transcoding",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/VideoCodec"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"resolutions",
|
||||
"videoCodecs"
|
||||
"enabled"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
||||
@@ -2302,10 +2302,6 @@ export type SystemConfigBackupsDto = {
|
||||
export type SystemConfigFFmpegRealtimeDto = {
|
||||
/** Enable real-time HLS transcoding (alpha) */
|
||||
enabled: boolean;
|
||||
/** Resolutions to use for real-time HLS transcoding */
|
||||
resolutions: HlsVideoResolution[];
|
||||
/** Video codecs to use for real-time HLS transcoding */
|
||||
videoCodecs: VideoCodec[];
|
||||
};
|
||||
export type SystemConfigFFmpegDto = {
|
||||
accel: TranscodeHWAccel;
|
||||
@@ -7633,13 +7629,6 @@ export enum CQMode {
|
||||
Cqp = "cqp",
|
||||
Icq = "icq"
|
||||
}
|
||||
export enum HlsVideoResolution {
|
||||
$480 = 480,
|
||||
$720 = 720,
|
||||
$1080 = 1080,
|
||||
$1440 = 1440,
|
||||
$2160 = 2160
|
||||
}
|
||||
export enum ToneMapping {
|
||||
Hable = "hable",
|
||||
Mobius = "mobius",
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
AudioCodec,
|
||||
Colorspace,
|
||||
CQMode,
|
||||
HlsVideoResolution,
|
||||
ImageFormat,
|
||||
LogLevel,
|
||||
OAuthTokenEndpointAuthMethod,
|
||||
@@ -49,8 +48,6 @@ export type SystemConfig = {
|
||||
tonemap: ToneMapping;
|
||||
realtime: {
|
||||
enabled: boolean;
|
||||
videoCodecs: VideoCodec[];
|
||||
resolutions: HlsVideoResolution[];
|
||||
};
|
||||
};
|
||||
integrityChecks: {
|
||||
@@ -250,8 +247,6 @@ export const defaults = Object.freeze<SystemConfig>({
|
||||
accelDecode: true,
|
||||
realtime: {
|
||||
enabled: false,
|
||||
videoCodecs: [VideoCodec.H264, VideoCodec.Hevc],
|
||||
resolutions: [HlsVideoResolution.p480, HlsVideoResolution.p720, HlsVideoResolution.p1080],
|
||||
},
|
||||
},
|
||||
integrityChecks: {
|
||||
|
||||
+9
-60
@@ -235,65 +235,14 @@ export const HLS_PLAYLIST_CONTENT_TYPE = 'application/vnd.apple.mpegurl';
|
||||
export const HLS_SEGMENT_DURATION = 2;
|
||||
export const HLS_SEGMENT_FILENAME_REGEX = /^seg_(\d+)\.m4s$/;
|
||||
export const HLS_VARIANTS = [
|
||||
{ resolution: 480, codec: VideoCodec.Av1, bitrate: 1_000_000 },
|
||||
{ resolution: 480, codec: VideoCodec.Hevc, bitrate: 1_200_000 },
|
||||
{ resolution: 480, codec: VideoCodec.H264, bitrate: 2_500_000 },
|
||||
{ resolution: 720, codec: VideoCodec.Av1, bitrate: 2_000_000 },
|
||||
{ resolution: 720, codec: VideoCodec.Hevc, bitrate: 2_500_000 },
|
||||
{ resolution: 720, codec: VideoCodec.H264, bitrate: 5_000_000 },
|
||||
{ resolution: 1080, codec: VideoCodec.Av1, bitrate: 4_000_000 },
|
||||
{ resolution: 1080, codec: VideoCodec.Hevc, bitrate: 4_500_000 },
|
||||
{ resolution: 1080, codec: VideoCodec.H264, bitrate: 8_000_000 },
|
||||
{ resolution: 1440, codec: VideoCodec.Av1, bitrate: 7_000_000 },
|
||||
{ resolution: 1440, codec: VideoCodec.Hevc, bitrate: 8_000_000 },
|
||||
{ resolution: 1440, codec: VideoCodec.H264, bitrate: 14_000_000 },
|
||||
{ resolution: 2160, codec: VideoCodec.Av1, bitrate: 12_000_000 },
|
||||
{ resolution: 2160, codec: VideoCodec.Hevc, bitrate: 14_000_000 },
|
||||
{ resolution: 2160, codec: VideoCodec.H264, bitrate: 25_000_000 },
|
||||
{ resolution: 480, codec: VideoCodec.Av1, bitrate: 1_000_000, codecString: 'av01.0.04M.08' },
|
||||
{ resolution: 480, codec: VideoCodec.Hevc, bitrate: 1_200_000, codecString: 'hvc1.1.6.L90.B0' },
|
||||
{ resolution: 480, codec: VideoCodec.H264, bitrate: 2_500_000, codecString: 'avc1.64001e' },
|
||||
{ resolution: 720, codec: VideoCodec.Av1, bitrate: 2_000_000, codecString: 'av01.0.08M.08' },
|
||||
{ resolution: 720, codec: VideoCodec.Hevc, bitrate: 2_500_000, codecString: 'hvc1.1.6.L93.B0' },
|
||||
{ resolution: 720, codec: VideoCodec.H264, bitrate: 5_000_000, codecString: 'avc1.64001f' },
|
||||
{ resolution: 1080, codec: VideoCodec.Av1, bitrate: 4_000_000, codecString: 'av01.0.09M.08' },
|
||||
{ resolution: 1080, codec: VideoCodec.Hevc, bitrate: 4_500_000, codecString: 'hvc1.1.6.L120.B0' },
|
||||
{ resolution: 1080, codec: VideoCodec.H264, bitrate: 8_000_000, codecString: 'avc1.640028' },
|
||||
];
|
||||
export const HLS_VERSION = 7;
|
||||
|
||||
export type CodecLevel = { maxFrame: number; maxRate: number; token: string };
|
||||
|
||||
// H.264 High profile: token is the hex level_idc.
|
||||
export const H264_LEVELS: CodecLevel[] = [
|
||||
{ maxFrame: 1620, maxRate: 40_500, token: '1e' }, // 3.0
|
||||
{ maxFrame: 3600, maxRate: 108_000, token: '1f' }, // 3.1
|
||||
{ maxFrame: 5120, maxRate: 216_000, token: '20' }, // 3.2
|
||||
{ maxFrame: 8192, maxRate: 245_760, token: '28' }, // 4.0
|
||||
{ maxFrame: 8704, maxRate: 522_240, token: '2a' }, // 4.2
|
||||
{ maxFrame: 22_080, maxRate: 589_824, token: '32' }, // 5.0
|
||||
{ maxFrame: 36_864, maxRate: 983_040, token: '33' }, // 5.1
|
||||
{ maxFrame: 36_864, maxRate: 2_073_600, token: '34' }, // 5.2
|
||||
{ maxFrame: 139_264, maxRate: 4_177_920, token: '3c' }, // 6.0
|
||||
{ maxFrame: 139_264, maxRate: 8_355_840, token: '3d' }, // 6.1
|
||||
{ maxFrame: 139_264, maxRate: 16_711_680, token: '3e' }, // 6.2
|
||||
];
|
||||
|
||||
// HEVC Main profile, Main tier: token is `L` + level_idc (level × 30).
|
||||
export const HEVC_LEVELS: CodecLevel[] = [
|
||||
{ maxFrame: 552_960, maxRate: 16_588_800, token: 'L90' }, // 3.0
|
||||
{ maxFrame: 983_040, maxRate: 33_177_600, token: 'L93' }, // 3.1
|
||||
{ maxFrame: 2_228_224, maxRate: 66_846_720, token: 'L120' }, // 4.0
|
||||
{ maxFrame: 2_228_224, maxRate: 133_693_440, token: 'L123' }, // 4.1
|
||||
{ maxFrame: 8_912_896, maxRate: 267_386_880, token: 'L150' }, // 5.0
|
||||
{ maxFrame: 8_912_896, maxRate: 534_773_760, token: 'L153' }, // 5.1
|
||||
{ maxFrame: 8_912_896, maxRate: 1_069_547_520, token: 'L156' }, // 5.2
|
||||
{ maxFrame: 35_651_584, maxRate: 1_069_547_520, token: 'L180' }, // 6.0
|
||||
{ maxFrame: 35_651_584, maxRate: 2_139_095_040, token: 'L183' }, // 6.1
|
||||
{ maxFrame: 35_651_584, maxRate: 4_278_190_080, token: 'L186' }, // 6.2
|
||||
];
|
||||
|
||||
// AV1 Main profile (0), Main tier (M): token is the two-digit seq_level_idx + `M`.
|
||||
export const AV1_LEVELS: CodecLevel[] = [
|
||||
{ maxFrame: 665_856, maxRate: 19_975_168, token: '04M' }, // 3.0
|
||||
{ maxFrame: 1_065_024, maxRate: 31_950_336, token: '05M' }, // 3.1
|
||||
{ maxFrame: 2_359_296, maxRate: 70_778_880, token: '08M' }, // 4.0
|
||||
{ maxFrame: 2_359_296, maxRate: 141_557_760, token: '09M' }, // 4.1
|
||||
{ maxFrame: 8_912_896, maxRate: 267_386_880, token: '12M' }, // 5.0
|
||||
{ maxFrame: 8_912_896, maxRate: 534_773_760, token: '13M' }, // 5.1
|
||||
{ maxFrame: 8_912_896, maxRate: 1_069_547_520, token: '14M' }, // 5.2
|
||||
{ maxFrame: 35_651_584, maxRate: 1_069_547_520, token: '16M' }, // 6.0
|
||||
{ maxFrame: 35_651_584, maxRate: 2_139_095_040, token: '17M' }, // 6.1
|
||||
{ maxFrame: 35_651_584, maxRate: 4_278_190_080, token: '18M' }, // 6.2
|
||||
];
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
AudioCodecSchema,
|
||||
ColorspaceSchema,
|
||||
CQModeSchema,
|
||||
HlsVideoResolutionSchema,
|
||||
ImageFormatSchema,
|
||||
LogLevelSchema,
|
||||
OAuthTokenEndpointAuthMethodSchema,
|
||||
@@ -116,8 +115,6 @@ const SystemConfigFFmpegSchema = z
|
||||
realtime: z
|
||||
.object({
|
||||
enabled: configBool.describe('Enable real-time HLS transcoding (alpha)'),
|
||||
videoCodecs: z.array(VideoCodecSchema).describe('Video codecs to use for real-time HLS transcoding'),
|
||||
resolutions: z.array(HlsVideoResolutionSchema).describe('Resolutions to use for real-time HLS transcoding'),
|
||||
})
|
||||
.meta({ id: 'SystemConfigFFmpegRealtimeDto' }),
|
||||
})
|
||||
|
||||
@@ -529,19 +529,6 @@ export enum CQMode {
|
||||
|
||||
export const CQModeSchema = z.enum(CQMode).describe('CQ mode').meta({ id: 'CQMode' });
|
||||
|
||||
export enum HlsVideoResolution {
|
||||
p480 = 480,
|
||||
p720 = 720,
|
||||
p1080 = 1080,
|
||||
p1440 = 1440,
|
||||
p2160 = 2160,
|
||||
}
|
||||
|
||||
export const HlsVideoResolutionSchema = z
|
||||
.enum(HlsVideoResolution)
|
||||
.describe('HLS video resolution')
|
||||
.meta({ id: 'HlsVideoResolution', type: 'integer' });
|
||||
|
||||
export enum Colorspace {
|
||||
Srgb = 'srgb',
|
||||
P3 = 'p3',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common';
|
||||
import { HlsVideoResolution, VideoCodec } from 'src/enum';
|
||||
import { TranscodeHardwareAcceleration } from 'src/enum';
|
||||
import { HlsService } from 'src/services/hls.service';
|
||||
import { eiffelTower, train, waterfall } from 'test/fixtures/media.stub';
|
||||
import { factory } from 'test/small.factory';
|
||||
@@ -96,79 +96,67 @@ seg_10.m4s
|
||||
|
||||
const sessionId = '00000000-0000-0000-0000-000000000000';
|
||||
|
||||
const eiffelExpectedMasterAv1 = `#EXTM3U
|
||||
const eiffelExpectedMasterDisabled = `#EXTM3U
|
||||
#EXT-X-VERSION:7
|
||||
#EXT-X-INDEPENDENT-SEGMENTS
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1350000,RESOLUTION=480x852,CODECS="av01.0.04M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=480x852,CODECS="av01.0.04M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/0/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1620000,RESOLUTION=480x852,CODECS="hvc1.1.6.L90.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1200000,RESOLUTION=480x852,CODECS="hvc1.1.6.L90.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/1/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=3375000,RESOLUTION=480x852,CODECS="avc1.64001e,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=480x852,CODECS="avc1.64001e,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/2/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2700000,RESOLUTION=720x1280,CODECS="av01.0.05M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=720x1280,CODECS="av01.0.08M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/3/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=3375000,RESOLUTION=720x1280,CODECS="hvc1.1.6.L93.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=720x1280,CODECS="hvc1.1.6.L93.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/4/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=6750000,RESOLUTION=720x1280,CODECS="avc1.64001f,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=720x1280,CODECS="avc1.64001f,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/5/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5400000,RESOLUTION=1080x1920,CODECS="av01.0.08M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=4000000,RESOLUTION=1080x1920,CODECS="av01.0.09M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/6/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=6075000,RESOLUTION=1080x1920,CODECS="hvc1.1.6.L120.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=4500000,RESOLUTION=1080x1920,CODECS="hvc1.1.6.L120.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/7/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=10800000,RESOLUTION=1080x1920,CODECS="avc1.640028,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=1080x1920,CODECS="avc1.640028,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/8/playlist.m3u8
|
||||
`;
|
||||
|
||||
const eiffelExpectedMasterNoAv1 = `#EXTM3U
|
||||
const eiffelExpectedMasterRkmpp = `#EXTM3U
|
||||
#EXT-X-VERSION:7
|
||||
#EXT-X-INDEPENDENT-SEGMENTS
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1620000,RESOLUTION=480x852,CODECS="hvc1.1.6.L90.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1200000,RESOLUTION=480x852,CODECS="hvc1.1.6.L90.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/1/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=3375000,RESOLUTION=480x852,CODECS="avc1.64001e,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=480x852,CODECS="avc1.64001e,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/2/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=3375000,RESOLUTION=720x1280,CODECS="hvc1.1.6.L93.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=720x1280,CODECS="hvc1.1.6.L93.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/4/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=6750000,RESOLUTION=720x1280,CODECS="avc1.64001f,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=720x1280,CODECS="avc1.64001f,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/5/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=6075000,RESOLUTION=1080x1920,CODECS="hvc1.1.6.L120.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=4500000,RESOLUTION=1080x1920,CODECS="hvc1.1.6.L120.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/7/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=10800000,RESOLUTION=1080x1920,CODECS="avc1.640028,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=1080x1920,CODECS="avc1.640028,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=24.910
|
||||
${sessionId}/8/playlist.m3u8
|
||||
`;
|
||||
|
||||
const waterfallExpectedMasterAv1 = `#EXTM3U
|
||||
const waterfallExpectedMasterDisabled = `#EXTM3U
|
||||
#EXT-X-VERSION:7
|
||||
#EXT-X-INDEPENDENT-SEGMENTS
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1350000,RESOLUTION=480x852,CODECS="av01.0.04M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1000000,RESOLUTION=480x852,CODECS="av01.0.04M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/0/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1620000,RESOLUTION=480x852,CODECS="hvc1.1.6.L90.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=1200000,RESOLUTION=480x852,CODECS="hvc1.1.6.L90.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/1/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=3375000,RESOLUTION=480x852,CODECS="avc1.64001f,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=480x852,CODECS="avc1.64001e,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/2/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2700000,RESOLUTION=720x1280,CODECS="av01.0.05M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=720x1280,CODECS="av01.0.08M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/3/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=3375000,RESOLUTION=720x1280,CODECS="hvc1.1.6.L93.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=2500000,RESOLUTION=720x1280,CODECS="hvc1.1.6.L93.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/4/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=6750000,RESOLUTION=720x1280,CODECS="avc1.64001f,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=720x1280,CODECS="avc1.64001f,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/5/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=5400000,RESOLUTION=1080x1920,CODECS="av01.0.08M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=4000000,RESOLUTION=1080x1920,CODECS="av01.0.09M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/6/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=6075000,RESOLUTION=1080x1920,CODECS="hvc1.1.6.L120.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=4500000,RESOLUTION=1080x1920,CODECS="hvc1.1.6.L120.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/7/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=10800000,RESOLUTION=1080x1920,CODECS="avc1.640028,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=1080x1920,CODECS="avc1.640028,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/8/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=9450000,RESOLUTION=1440x2560,CODECS="av01.0.12M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/9/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=10800000,RESOLUTION=1440x2560,CODECS="hvc1.1.6.L150.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/10/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=18900000,RESOLUTION=1440x2560,CODECS="avc1.640032,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/11/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=16200000,RESOLUTION=2160x3840,CODECS="av01.0.12M.08,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/12/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=18900000,RESOLUTION=2160x3840,CODECS="hvc1.1.6.L150.B0,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/13/playlist.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=33750000,RESOLUTION=2160x3840,CODECS="avc1.640033,mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=29.830
|
||||
${sessionId}/14/playlist.m3u8
|
||||
`;
|
||||
|
||||
describe(HlsService.name, () => {
|
||||
@@ -183,24 +171,9 @@ describe(HlsService.name, () => {
|
||||
const auth = factory.auth();
|
||||
const assetId = 'asset-1';
|
||||
|
||||
const allCodecs = [VideoCodec.Av1, VideoCodec.Hevc, VideoCodec.H264];
|
||||
const allResolutions = [
|
||||
HlsVideoResolution.p480,
|
||||
HlsVideoResolution.p720,
|
||||
HlsVideoResolution.p1080,
|
||||
HlsVideoResolution.p1440,
|
||||
HlsVideoResolution.p2160,
|
||||
];
|
||||
|
||||
const setup = (
|
||||
asset: typeof eiffelTower | typeof waterfall,
|
||||
videoCodecs?: VideoCodec[],
|
||||
resolutions?: HlsVideoResolution[],
|
||||
) => {
|
||||
const setup = (asset: typeof eiffelTower | typeof waterfall, accel: TranscodeHardwareAcceleration) => {
|
||||
mocks.access.asset.checkOwnerAccess.mockResolvedValue(new Set([assetId]));
|
||||
mocks.systemMetadata.get.mockResolvedValue({
|
||||
ffmpeg: { realtime: { enabled: true, videoCodecs, resolutions } },
|
||||
});
|
||||
mocks.systemMetadata.get.mockResolvedValue({ ffmpeg: { realtime: { enabled: true }, accel } });
|
||||
mocks.videoStream.getForMainPlaylist.mockResolvedValue(asset);
|
||||
mocks.crypto.randomUUID.mockReturnValue(sessionId);
|
||||
mocks.websocket.serverSend.mockImplementation((event, ...rest) => {
|
||||
@@ -211,19 +184,19 @@ describe(HlsService.name, () => {
|
||||
});
|
||||
};
|
||||
|
||||
it('offers AV1, HEVC, and H.264 when AV1 is configured and the accelerator supports it', async () => {
|
||||
setup(eiffelTower, allCodecs);
|
||||
await expect(sut.getMainPlaylist(auth, assetId)).resolves.toBe(eiffelExpectedMasterAv1);
|
||||
it('returns main playlist for eiffel-tower (1080p portrait, no acceleration)', async () => {
|
||||
setup(eiffelTower, TranscodeHardwareAcceleration.Disabled);
|
||||
await expect(sut.getMainPlaylist(auth, assetId)).resolves.toBe(eiffelExpectedMasterDisabled);
|
||||
});
|
||||
|
||||
it('omits AV1 when it is not in the configured codecs', async () => {
|
||||
setup(eiffelTower);
|
||||
await expect(sut.getMainPlaylist(auth, assetId)).resolves.toBe(eiffelExpectedMasterNoAv1);
|
||||
it('returns main playlist for eiffel-tower with RKMPP (no AV1 variants)', async () => {
|
||||
setup(eiffelTower, TranscodeHardwareAcceleration.Rkmpp);
|
||||
await expect(sut.getMainPlaylist(auth, assetId)).resolves.toBe(eiffelExpectedMasterRkmpp);
|
||||
});
|
||||
|
||||
it('offers every resolution up to the source and derives 4K codec levels (waterfall, 4K, 29.83fps)', async () => {
|
||||
setup(waterfall, allCodecs, allResolutions);
|
||||
await expect(sut.getMainPlaylist(auth, assetId)).resolves.toBe(waterfallExpectedMasterAv1);
|
||||
it('returns main playlist for waterfall (4K landscape) with no acceleration', async () => {
|
||||
setup(waterfall, TranscodeHardwareAcceleration.Disabled);
|
||||
await expect(sut.getMainPlaylist(auth, assetId)).resolves.toBe(waterfallExpectedMasterDisabled);
|
||||
});
|
||||
|
||||
it('throws BadRequestException when realtime transcoding is disabled', async () => {
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { constants } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { HLS_SEGMENT_DURATION, HLS_SEGMENT_FILENAME_REGEX, HLS_VARIANTS, HLS_VERSION } from 'src/constants';
|
||||
import {
|
||||
HLS_SEGMENT_DURATION,
|
||||
HLS_SEGMENT_FILENAME_REGEX,
|
||||
HLS_VARIANTS,
|
||||
HLS_VERSION,
|
||||
SUPPORTED_HWA_CODECS,
|
||||
} from 'src/constants';
|
||||
import { StorageCore } from 'src/cores/storage.core';
|
||||
import { OnEvent } from 'src/decorators';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
@@ -12,7 +18,7 @@ import { BaseService } from 'src/services/base.service';
|
||||
import { VideoPacketInfo, VideoStreamInfo } from 'src/types';
|
||||
import { PendingEvents } from 'src/utils/event';
|
||||
import { ImmichFileResponse } from 'src/utils/file';
|
||||
import { getCodecString, getOutputSize } from 'src/utils/media';
|
||||
import { getOutputSize } from 'src/utils/media';
|
||||
|
||||
type AssetWithStreamInfo = { videoStream: VideoStreamInfo & { timeBase: number }; packets: VideoPacketInfo };
|
||||
type Segmentation = { fps: number; framesPerSegment: number; segmentCount: number; segmentDuration: number };
|
||||
@@ -125,21 +131,18 @@ export class HlsService extends BaseService {
|
||||
}
|
||||
|
||||
private generateMainPlaylist(sessionId: string, ffmpeg: SystemConfigFFmpegDto, asset: AssetWithStreamInfo) {
|
||||
const fps = (asset.packets.packetCount * asset.videoStream.timeBase) / asset.packets.totalDuration;
|
||||
const roundedFps = fps.toFixed(3);
|
||||
const fps = ((asset.packets.packetCount * asset.videoStream.timeBase) / asset.packets.totalDuration).toFixed(3);
|
||||
const sourceResolution = Math.min(asset.videoStream.height, asset.videoStream.width);
|
||||
const targetResolution = Math.max(sourceResolution, HLS_VARIANTS[0].resolution);
|
||||
const lines = ['#EXTM3U', `#EXT-X-VERSION:${HLS_VERSION}`, '#EXT-X-INDEPENDENT-SEGMENTS'];
|
||||
const { videoCodecs, resolutions } = ffmpeg.realtime;
|
||||
for (let i = 0; i < HLS_VARIANTS.length; i++) {
|
||||
const { resolution, bitrate, codec } = HLS_VARIANTS[i];
|
||||
if (resolution > targetResolution || !videoCodecs.includes(codec) || !resolutions.includes(resolution)) {
|
||||
const { resolution, bitrate, codec, codecString } = HLS_VARIANTS[i];
|
||||
if (resolution > targetResolution || !SUPPORTED_HWA_CODECS[ffmpeg.accel].includes(codec)) {
|
||||
continue;
|
||||
}
|
||||
const { width, height } = getOutputSize(asset.videoStream, resolution);
|
||||
const codecString = getCodecString(codec, width, height, fps);
|
||||
lines.push(
|
||||
`#EXT-X-STREAM-INF:BANDWIDTH=${Math.round(bitrate * 1.35)},RESOLUTION=${width}x${height},CODECS="${codecString},mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=${roundedFps}`,
|
||||
`#EXT-X-STREAM-INF:BANDWIDTH=${bitrate},RESOLUTION=${width}x${height},CODECS="${codecString},mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=${fps}`,
|
||||
`${sessionId}/${i}/playlist.m3u8`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
AudioCodec,
|
||||
Colorspace,
|
||||
CQMode,
|
||||
HlsVideoResolution,
|
||||
ImageFormat,
|
||||
LogLevel,
|
||||
OAuthTokenEndpointAuthMethod,
|
||||
@@ -77,8 +76,6 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
||||
tonemap: ToneMapping.Hable,
|
||||
realtime: {
|
||||
enabled: false,
|
||||
videoCodecs: [VideoCodec.H264, VideoCodec.Hevc],
|
||||
resolutions: [HlsVideoResolution.p480, HlsVideoResolution.p720, HlsVideoResolution.p1080],
|
||||
},
|
||||
},
|
||||
integrityChecks: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AUDIO_ENCODER, AV1_LEVELS, CodecLevel, H264_LEVELS, HEVC_LEVELS, SUPPORTED_HWA_CODECS } from 'src/constants';
|
||||
import { AUDIO_ENCODER, SUPPORTED_HWA_CODECS } from 'src/constants';
|
||||
import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto';
|
||||
import {
|
||||
ColorMatrix,
|
||||
@@ -36,29 +36,6 @@ export const getOutputSize = (videoStream: VideoStreamInfo, targetRes: number) =
|
||||
return isVideoVertical(videoStream) ? { width: targetRes, height: larger } : { width: larger, height: targetRes };
|
||||
};
|
||||
|
||||
const pickLevel = (levels: CodecLevel[], frame: number, rate: number) =>
|
||||
levels.find((level) => frame <= level.maxFrame && rate <= level.maxRate) ?? levels.at(-1)!;
|
||||
|
||||
export const getCodecString = (codec: VideoCodec, width: number, height: number, fps: number): string => {
|
||||
switch (codec) {
|
||||
case VideoCodec.H264: {
|
||||
const macroblocks = Math.ceil(width / 16) * Math.ceil(height / 16);
|
||||
return `avc1.6400${pickLevel(H264_LEVELS, macroblocks, macroblocks * fps).token}`;
|
||||
}
|
||||
case VideoCodec.Hevc: {
|
||||
const samples = width * height;
|
||||
return `hvc1.1.6.${pickLevel(HEVC_LEVELS, samples, samples * fps).token}.B0`;
|
||||
}
|
||||
case VideoCodec.Av1: {
|
||||
const samples = width * height;
|
||||
return `av01.0.${pickLevel(AV1_LEVELS, samples, samples * fps).token}.08`;
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Codec '${codec}' does not support HLS codec strings`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export class BaseConfig implements VideoCodecSWConfig {
|
||||
readonly presets = ['veryslow', 'slower', 'slow', 'medium', 'fast', 'faster', 'veryfast', 'superfast', 'ultrafast'];
|
||||
protected constructor(
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
import {
|
||||
AudioCodec,
|
||||
CQMode,
|
||||
HlsVideoResolution,
|
||||
ToneMapping,
|
||||
TranscodeHWAccel,
|
||||
TranscodePolicy,
|
||||
@@ -402,45 +401,9 @@
|
||||
title={$t('admin.transcoding_realtime_enabled')}
|
||||
subtitle={$t('admin.transcoding_realtime_enabled_description')}
|
||||
bind:checked={configToEdit.ffmpeg.realtime.enabled}
|
||||
isEdited={configToEdit.ffmpeg.realtime.enabled !== config.ffmpeg.realtime.enabled}
|
||||
isEdited={configToEdit.ffmpeg.realtime.enabled !== configToEdit.ffmpeg.realtime.enabled}
|
||||
{disabled}
|
||||
/>
|
||||
|
||||
<SettingCheckboxes
|
||||
label={$t('admin.transcoding_realtime_video_codecs')}
|
||||
desc={$t('admin.transcoding_realtime_video_codecs_description')}
|
||||
disabled={disabled || !configToEdit.ffmpeg.realtime.enabled}
|
||||
bind:value={configToEdit.ffmpeg.realtime.videoCodecs}
|
||||
name="realtimeVideoCodecs"
|
||||
options={[
|
||||
{ value: VideoCodec.H264, text: 'H.264' },
|
||||
{ value: VideoCodec.Hevc, text: 'HEVC' },
|
||||
{ value: VideoCodec.Av1, text: 'AV1' },
|
||||
]}
|
||||
isEdited={!isEqual(
|
||||
sortBy(configToEdit.ffmpeg.realtime.videoCodecs),
|
||||
sortBy(config.ffmpeg.realtime.videoCodecs),
|
||||
)}
|
||||
/>
|
||||
|
||||
<SettingCheckboxes
|
||||
label={$t('admin.transcoding_realtime_resolutions')}
|
||||
desc={$t('admin.transcoding_realtime_resolutions_description')}
|
||||
disabled={disabled || !configToEdit.ffmpeg.realtime.enabled}
|
||||
bind:value={configToEdit.ffmpeg.realtime.resolutions}
|
||||
name="realtimeResolutions"
|
||||
options={[
|
||||
{ value: HlsVideoResolution.$480, text: '480p' },
|
||||
{ value: HlsVideoResolution.$720, text: '720p' },
|
||||
{ value: HlsVideoResolution.$1080, text: '1080p' },
|
||||
{ value: HlsVideoResolution.$1440, text: '1440p' },
|
||||
{ value: HlsVideoResolution.$2160, text: '2160p' },
|
||||
]}
|
||||
isEdited={!isEqual(
|
||||
sortBy(configToEdit.ffmpeg.realtime.resolutions),
|
||||
sortBy(config.ffmpeg.realtime.resolutions),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</SettingAccordion>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="ts" generics="T extends string | number">
|
||||
<script lang="ts" generics="T extends string">
|
||||
import { Checkbox, Label } from '@immich/ui';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { quintOut } from 'svelte/easing';
|
||||
|
||||
Reference in New Issue
Block a user