Compare commits
No commits in common. "main" and "3.2.0" have entirely different histories.
|
@ -0,0 +1,22 @@
|
|||
{{ range .Versions }}<a name="{{ .Tag.Name }}"></a>
|
||||
|
||||
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} ({{ datetime "2006-01-02" .Tag.Date }})
|
||||
|
||||
- [Release note](https://libretime.org/docs/releases/{{ .Tag.Name }}/)
|
||||
|
||||
{{ range .CommitGroups -}}
|
||||
### {{ .Title }}
|
||||
|
||||
{{ range reverse .Commits -}}
|
||||
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
|
||||
{{- if .RevertCommits -}}
|
||||
### Reverts
|
||||
|
||||
{{ range .RevertCommits -}}
|
||||
- {{ .Revert.Header }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
|
@ -0,0 +1,25 @@
|
|||
style: github
|
||||
template: CHANGELOG.md.tpl
|
||||
info:
|
||||
title: CHANGELOG
|
||||
repository_url: https://github.com/libretime/libretime
|
||||
options:
|
||||
commits:
|
||||
filters:
|
||||
Type: [feat, fix, docs, test, ci]
|
||||
sort_by: Date
|
||||
commit_groups:
|
||||
title_maps:
|
||||
feat: Features
|
||||
fix: Bug Fixes
|
||||
docs: Documentation
|
||||
test: Tests
|
||||
ci: CI
|
||||
sort_by: Custom
|
||||
title_order: [feat, fix, docs, test, ci]
|
||||
header:
|
||||
pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$"
|
||||
pattern_maps:
|
||||
- Type
|
||||
- Scope
|
||||
- Subject
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
|
||||
"bootstrap-sha": "26737abad231d96fc198fbf12c043f2d867be79c",
|
||||
"include-component-in-tag": false,
|
||||
"include-v-in-tag": false,
|
||||
"packages": {
|
||||
".": {
|
||||
"release-type": "simple",
|
||||
"package-name": "libretime",
|
||||
"extra-files": [
|
||||
"analyzer/setup.py",
|
||||
"api/setup.py",
|
||||
"api-client/setup.py",
|
||||
"playout/setup.py",
|
||||
"shared/setup.py",
|
||||
"worker/setup.py"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{".":"4.2.0"}
|
|
@ -10,7 +10,7 @@
|
|||
"automerge": true,
|
||||
"automergeType": "branch"
|
||||
},
|
||||
"baseBranches": ["main"],
|
||||
"baseBranches": ["main", "stable"],
|
||||
"labels": ["dependencies"],
|
||||
"packageRules": [
|
||||
{
|
||||
|
|
|
@ -25,11 +25,11 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ inputs.context }}-${{ hashFiles(format('{0}/{1}', inputs.context, '**/setup.py')) }}
|
||||
|
@ -63,9 +63,9 @@ jobs:
|
|||
shell: bash
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ matrix.release }}-pip-${{ inputs.context }}-${{ hashFiles(format('{0}/{1}', inputs.context, '**/setup.py')) }}
|
||||
|
@ -77,7 +77,7 @@ jobs:
|
|||
working-directory: ${{ inputs.context }}
|
||||
|
||||
- name: Report coverage
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ${{ inputs.context }}/coverage.xml
|
||||
flags: ${{ inputs.context }}
|
||||
|
|
|
@ -3,7 +3,7 @@ name: Analyzer
|
|||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/analyzer.yml
|
||||
|
@ -12,7 +12,7 @@ on:
|
|||
- tools/python*
|
||||
|
||||
pull_request:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/analyzer.yml
|
||||
|
|
|
@ -3,7 +3,7 @@ name: API Client
|
|||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/api-client.yml
|
||||
|
@ -12,7 +12,7 @@ on:
|
|||
- tools/python*
|
||||
|
||||
pull_request:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/api-client.yml
|
||||
|
|
|
@ -21,15 +21,15 @@ jobs:
|
|||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-api-${{ hashFiles('api/**/setup.py') }}
|
||||
|
@ -42,7 +42,7 @@ jobs:
|
|||
|
||||
- name: Get pull request commit range
|
||||
if: github.event_name == 'pull_request'
|
||||
run: echo "COMMIT_RANGE=${{ github.sha }}~1...${{ github.sha }}" >> $GITHUB_ENV
|
||||
run: echo "COMMIT_RANGE=origin/${{ github.base_ref }}..${{ github.sha }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Get push commit range
|
||||
if: github.event_name == 'push'
|
||||
|
@ -71,11 +71,11 @@ jobs:
|
|||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
with:
|
||||
repository: libretime/client
|
||||
path: client
|
||||
|
|
|
@ -3,7 +3,7 @@ name: API
|
|||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/api.yml
|
||||
|
@ -12,7 +12,7 @@ on:
|
|||
- tools/python*
|
||||
|
||||
pull_request:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/api.yml
|
||||
|
@ -62,9 +62,9 @@ jobs:
|
|||
LIBRETIME_DATABASE_HOST: postgres
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ matrix.release }}-pip-api-${{ hashFiles('api/**/setup.py') }}
|
||||
|
@ -76,7 +76,7 @@ jobs:
|
|||
working-directory: api
|
||||
|
||||
- name: Report coverage
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: api/coverage.xml
|
||||
flags: api
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
name: Backport
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
backport:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Only react to merged PRs for security reasons.
|
||||
# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target.
|
||||
if: >
|
||||
github.event.pull_request.merged
|
||||
&& (
|
||||
github.event.action == 'closed'
|
||||
|| (
|
||||
github.event.action == 'labeled'
|
||||
&& contains(github.event.label.name, 'backport')
|
||||
)
|
||||
)
|
||||
steps:
|
||||
- uses: jooola/backport@main
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
title_template: <%= title %> (<%= base %>)
|
||||
body_template: |
|
||||
Backport <%= mergeCommitSha %> from #<%= number %>.
|
||||
|
||||
BEGIN_COMMIT_OVERRIDE
|
||||
<%= title %>
|
||||
END_COMMIT_OVERRIDE
|
|
@ -3,9 +3,9 @@ name: Container
|
|||
on:
|
||||
push:
|
||||
tags: ["[0-9]+.[0-9]+.[0-9]+*"]
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
pull_request:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
@ -20,11 +20,11 @@ jobs:
|
|||
|
||||
if: ${{ github.repository_owner == 'libretime' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
|
||||
- name: Update Docker Hub description
|
||||
if: github.event_name == 'push'
|
||||
uses: peter-evans/dockerhub-description@v4
|
||||
uses: peter-evans/dockerhub-description@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
@ -44,7 +44,7 @@ jobs:
|
|||
type=semver,pattern={{major}}.{{minor}}
|
||||
|
||||
- name: Upload metadata bake file
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: meta-${{ matrix.target }}
|
||||
path: ${{ steps.meta.outputs.bake-file }}
|
||||
|
@ -55,7 +55,7 @@ jobs:
|
|||
|
||||
if: ${{ github.repository_owner == 'libretime' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
|
@ -76,9 +76,7 @@ jobs:
|
|||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Download all metadata bake files
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: meta-*
|
||||
uses: actions/download-artifact@v3
|
||||
|
||||
- name: Guess LIBRETIME_VERSION
|
||||
run: |
|
||||
|
@ -86,7 +84,7 @@ jobs:
|
|||
echo "LIBRETIME_VERSION=$(cat VERSION | tr -d [:blank:])" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
uses: docker/bake-action@v5
|
||||
uses: docker/bake-action@v4
|
||||
with:
|
||||
pull: true
|
||||
push: ${{ github.event_name == 'push' }}
|
||||
|
|
|
@ -30,7 +30,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
|
||||
- name: Login to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
|
@ -78,7 +78,7 @@ jobs:
|
|||
EOF
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.repository_owner == 'libretime' }}
|
||||
|
|
|
@ -2,14 +2,14 @@ name: Docs
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/vale/**
|
||||
- .github/workflows/docs.yml
|
||||
- docs/**
|
||||
|
||||
pull_request:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/vale/**
|
||||
- .github/workflows/docs.yml
|
||||
|
@ -24,9 +24,9 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
/usr/local/bin/vale*
|
||||
|
@ -64,11 +64,11 @@ jobs:
|
|||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
with:
|
||||
repository: libretime/website
|
||||
path: website
|
||||
|
|
|
@ -20,9 +20,9 @@ jobs:
|
|||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
|
||||
|
@ -38,9 +38,9 @@ jobs:
|
|||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: .lycheecache
|
||||
key: housekeeping-find-broken-links-${{ github.sha }}
|
||||
|
@ -48,12 +48,13 @@ jobs:
|
|||
|
||||
- name: Check Links
|
||||
id: lychee
|
||||
uses: lycheeverse/lychee-action@v2.1.0
|
||||
uses: lycheeverse/lychee-action@v1.8.0
|
||||
with:
|
||||
args: >-
|
||||
'**/*.md'
|
||||
--require-https
|
||||
--exclude-all-private
|
||||
--exclude-mail
|
||||
--exclude 'example\.(com|org)'
|
||||
--exclude '\$server_name\$request_uri'
|
||||
--exclude '%7Bvars.version%7D'
|
||||
|
@ -62,8 +63,6 @@ jobs:
|
|||
--exclude 'https://www.ascap.com'
|
||||
--exclude 'https://www.youtube-nocookie.com'
|
||||
--exclude 'github\.com/libretime/libretime/(issues|pulls)'
|
||||
--exclude 'https://packages.ubuntu.com/bionic/php7.2'
|
||||
--exclude 'https://packages.ubuntu.com/bionic/python3'
|
||||
--cache
|
||||
--max-cache-age 2d
|
||||
fail: true
|
||||
|
@ -76,7 +75,7 @@ jobs:
|
|||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@v8
|
||||
with:
|
||||
stale-issue-message: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
|
|
|
@ -3,14 +3,14 @@ name: Legacy
|
|||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/legacy.yml
|
||||
- api/**
|
||||
- legacy/**
|
||||
|
||||
pull_request:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/legacy.yml
|
||||
- api/**
|
||||
|
@ -33,7 +33,7 @@ jobs:
|
|||
- php-version: "7.4" # Focal, Bullseye
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
|
@ -53,7 +53,7 @@ jobs:
|
|||
env:
|
||||
ENVIRONMENT: testing
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
|
||||
- name: Setup PostgreSQL
|
||||
run: |
|
||||
|
@ -62,7 +62,6 @@ jobs:
|
|||
sudo -u postgres psql -c 'CREATE DATABASE libretime;'
|
||||
sudo -u postgres psql -c "CREATE USER libretime WITH PASSWORD 'libretime';"
|
||||
sudo -u postgres psql -c 'GRANT CONNECT ON DATABASE libretime TO libretime;'
|
||||
sudo -u postgres psql -c 'ALTER DATABASE libretime OWNER TO libretime;'
|
||||
sudo -u postgres psql -c 'ALTER USER libretime CREATEDB;'
|
||||
|
||||
- name: Setup PHP
|
||||
|
@ -75,7 +74,7 @@ jobs:
|
|||
run: |
|
||||
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
|
@ -95,7 +94,7 @@ jobs:
|
|||
github.event_name == 'workflow_dispatch'
|
||||
)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
with:
|
||||
token: ${{ secrets.LIBRETIME_BOT_TOKEN }}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ name: Playout
|
|||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/playout.yml
|
||||
|
@ -13,7 +13,7 @@ on:
|
|||
- tools/python*
|
||||
|
||||
pull_request:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/playout.yml
|
||||
|
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
name: Validate PR title
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v5.5.3
|
||||
- uses: amannn/action-semantic-pull-request@v5.3.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
@ -27,8 +27,6 @@ jobs:
|
|||
ui
|
||||
worker
|
||||
deps
|
||||
main
|
||||
stable
|
||||
subjectPattern: ^(?![A-Z]).+$
|
||||
subjectPatternError: |
|
||||
The subject "{subject}" found in the pull request title "{title}"
|
||||
|
|
|
@ -3,10 +3,10 @@ name: Project
|
|||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize, edited]
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
@ -15,26 +15,26 @@ jobs:
|
|||
pre-commit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-project-pre-commit-pip-${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-project-pre-commit-pip
|
||||
|
||||
- uses: pre-commit/action@v3.0.1
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
|
||||
test-tools:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- run: make all
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
name: Release-Please
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- stable
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
# Do not run on forks.
|
||||
if: github.repository == 'libretime/libretime'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: google-github-actions/release-please-action@v4
|
||||
with:
|
||||
token: ${{ secrets.LIBRETIME_BOT_TOKEN }}
|
||||
config-file: .github/release-please-config.json
|
||||
manifest-file: .github/release-please-manifest.json
|
||||
target-branch: ${{ github.ref_name }}
|
|
@ -9,7 +9,7 @@ jobs:
|
|||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 7.4
|
||||
|
@ -22,9 +22,12 @@ jobs:
|
|||
- name: Build tarball
|
||||
run: make tarball
|
||||
|
||||
- name: Upload tarball
|
||||
uses: softprops/action-gh-release@v2
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body_path: docs/releases/${{ github.ref_name }}.md
|
||||
draft: true
|
||||
prerelease: true
|
||||
files: |
|
||||
libretime-*.tar.gz
|
||||
sha256sums.txt
|
||||
|
|
|
@ -3,7 +3,7 @@ name: Shared
|
|||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/shared.yml
|
||||
|
@ -11,7 +11,7 @@ on:
|
|||
- tools/python*
|
||||
|
||||
pull_request:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/shared.yml
|
||||
|
|
|
@ -3,7 +3,7 @@ name: Worker
|
|||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/worker.yml
|
||||
|
@ -11,7 +11,7 @@ on:
|
|||
- tools/python*
|
||||
|
||||
pull_request:
|
||||
branches: [main, stable-*]
|
||||
branches: [main, stable]
|
||||
paths:
|
||||
- .github/workflows/_python.yml
|
||||
- .github/workflows/worker.yml
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-case-conflict
|
||||
|
@ -29,37 +29,37 @@ repos:
|
|||
exclude: ^api
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v3.1.0
|
||||
rev: v3.0.3
|
||||
hooks:
|
||||
- id: prettier
|
||||
files: \.(md|mdx|yml|yaml|js|jsx|ts|tsx|json|css)$
|
||||
exclude: ^(legacy/public(?!/js/airtime)|CHANGELOG.md$|.github/release-please-manifest.json)
|
||||
exclude: ^legacy/public(?!/js/airtime)
|
||||
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.19.0
|
||||
rev: v3.15.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py38-plus]
|
||||
|
||||
- repo: https://github.com/adamchainz/django-upgrade
|
||||
rev: 1.22.2
|
||||
rev: 1.15.0
|
||||
hooks:
|
||||
- id: django-upgrade
|
||||
args: [--target-version, "4.2"]
|
||||
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.13.2
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
args: [--resolve-all-configs]
|
||||
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 24.10.0
|
||||
rev: 23.9.1
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.3.0
|
||||
rev: v2.2.6
|
||||
hooks:
|
||||
- id: codespell
|
||||
args: [--ignore-words=.codespellignore]
|
||||
|
@ -110,11 +110,3 @@ repos:
|
|||
pass_filenames: false
|
||||
language: script
|
||||
files: ^legacy
|
||||
|
||||
- id: api-schema-update
|
||||
name: api-schema-update
|
||||
description: Ensure API schema is up to date
|
||||
entry: make -C api schema
|
||||
pass_filenames: false
|
||||
language: system
|
||||
files: ^api
|
||||
|
|
156
CHANGELOG.md
156
CHANGELOG.md
|
@ -1,140 +1,4 @@
|
|||
# Changelog
|
||||
|
||||
## [4.2.0](https://github.com/libretime/libretime/compare/4.1.0...4.2.0) (2024-06-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **legacy:** add current date macro to string block criteria ([#3013](https://github.com/libretime/libretime/issues/3013)) ([451652b](https://github.com/libretime/libretime/commit/451652bc4002b142ab9cf33ae517451c4966134f))
|
||||
* **legacy:** add filename block criteria ([#3015](https://github.com/libretime/libretime/issues/3015)) ([4642b6c](https://github.com/libretime/libretime/commit/4642b6c08ef813ab5dc7354f73141239f5c145e0))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* pin pip version to <24.1 to allow installing pytz (celery) ([#3043](https://github.com/libretime/libretime/issues/3043)) ([646bc81](https://github.com/libretime/libretime/commit/646bc817246a1e3e0d8107c2b69d726681c643b6))
|
||||
* playlist allocates inaccurate time to smartblocks ([#3026](https://github.com/libretime/libretime/issues/3026)) ([2b43e51](https://github.com/libretime/libretime/commit/2b43e51ed140bf307e491f0fcb7b84f95709d604))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* optimize the api image health check ([#3038](https://github.com/libretime/libretime/issues/3038)) ([d99d6e1](https://github.com/libretime/libretime/commit/d99d6e1a68f20b3f4255296cd22ac80a90adc020))
|
||||
* optimize the rabbitmq health check ([#3037](https://github.com/libretime/libretime/issues/3037)) ([9684214](https://github.com/libretime/libretime/commit/96842144257855df86085b052ed8ff87562bc049))
|
||||
|
||||
## [4.1.0](https://github.com/libretime/libretime/compare/4.0.0...4.1.0) (2024-05-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** implement file deletion ([#2960](https://github.com/libretime/libretime/issues/2960)) ([9757b1b](https://github.com/libretime/libretime/commit/9757b1b78c98a33f233163c77eb1b2ad6e0f0efe))
|
||||
* build schedule events exclusively in playout ([#2946](https://github.com/libretime/libretime/issues/2946)) ([40b4fc7](https://github.com/libretime/libretime/commit/40b4fc7f66004ee3bcb61c9961ec2c48bbcbc6cb))
|
||||
* **legacy:** add aac/opus support to dashboard player ([#2881](https://github.com/libretime/libretime/issues/2881)) ([95283ef](https://github.com/libretime/libretime/commit/95283efc1f9a63376a99184ef69b699beba45802))
|
||||
* **legacy:** disable public radio page and redirect to login ([#2903](https://github.com/libretime/libretime/issues/2903)) ([170d095](https://github.com/libretime/libretime/commit/170d09545e4fcfeeb95f9fc5c355329764501854))
|
||||
* **legacy:** trim overbooked shows after autoloading a playlist ([#2897](https://github.com/libretime/libretime/issues/2897)) ([a95ce3d](https://github.com/libretime/libretime/commit/a95ce3d2296bb864b379dcce14090bd821c1dfc9))
|
||||
* **legacy:** visual cue point editor ([#2947](https://github.com/libretime/libretime/issues/2947)) ([da02e74](https://github.com/libretime/libretime/commit/da02e74f2115cb76a6435fab5ab2667a8c622b98))
|
||||
* start celery worker programmatically ([#2988](https://github.com/libretime/libretime/issues/2988)) ([9c548b3](https://github.com/libretime/libretime/commit/9c548b365ec114c6789d2a69e66cc721da6ae100))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **analyzer:** backslash non utf-8 data when probing replaygain ([#2931](https://github.com/libretime/libretime/issues/2931)) ([29f73e0](https://github.com/libretime/libretime/commit/29f73e0dcb1fd668a79a2ffedc33e16172277376)), closes [#2910](https://github.com/libretime/libretime/issues/2910)
|
||||
* apply replay gain preferences on scheduled files ([#2945](https://github.com/libretime/libretime/issues/2945)) ([35d0dec](https://github.com/libretime/libretime/commit/35d0dec4a887cdaea2d73dc9bee60eb6624a2aca))
|
||||
* **deps:** update dependency friendsofphp/php-cs-fixer to <3.49.1 ([#2899](https://github.com/libretime/libretime/issues/2899)) ([3e05748](https://github.com/libretime/libretime/commit/3e05748d2d1180b8dad55b6f997e6aa7117735f1))
|
||||
* **deps:** update dependency friendsofphp/php-cs-fixer to <3.51.1 ([#2963](https://github.com/libretime/libretime/issues/2963)) ([22c303c](https://github.com/libretime/libretime/commit/22c303cfffdc777177bd74273e2c24da58cf1682))
|
||||
* **deps:** update dependency friendsofphp/php-cs-fixer to <3.53.1 ([#2972](https://github.com/libretime/libretime/issues/2972)) ([9192aaa](https://github.com/libretime/libretime/commit/9192aaa2bb2dada470e03537493160d9b14a42f4))
|
||||
* **deps:** update dependency gunicorn to v22 (security) ([#2993](https://github.com/libretime/libretime/issues/2993)) ([a2cf769](https://github.com/libretime/libretime/commit/a2cf7697a97bbc4faf89fd7bc9ba9ecc235bf873))
|
||||
* incorrect docker compose version ([#2975](https://github.com/libretime/libretime/issues/2975)) ([634e6e2](https://github.com/libretime/libretime/commit/634e6e236d908994d586c946bbe28bcba8a357fa))
|
||||
* **installer:** setup the worker entrypoint ([#2996](https://github.com/libretime/libretime/issues/2996)) ([71b20ae](https://github.com/libretime/libretime/commit/71b20ae3c974680d814062c5a0bfa51a105dde61))
|
||||
* **legacy:** allow deleting file with api token ([#2995](https://github.com/libretime/libretime/issues/2995)) ([86da46e](https://github.com/libretime/libretime/commit/86da46ee3a54676298e30301846be890d1ea93ae))
|
||||
* **legacy:** allow updating track types code ([#2955](https://github.com/libretime/libretime/issues/2955)) ([270aa08](https://github.com/libretime/libretime/commit/270aa08ae6c7207de1cc3ea552dabeb018bcfe0d))
|
||||
* **legacy:** avoid crash when lot of streams in configuration ([#2915](https://github.com/libretime/libretime/issues/2915)) ([12dd477](https://github.com/libretime/libretime/commit/12dd47731290bf539be7a2a81571f8ada223e9c4))
|
||||
* **legacy:** ensure validation is performed on the track type form ([#2985](https://github.com/libretime/libretime/issues/2985)) ([5ad69bf](https://github.com/libretime/libretime/commit/5ad69bf0b76ff2e5065551b6a7d154cb26834605))
|
||||
* **legacy:** fix hidden fields in edit file form ([#2932](https://github.com/libretime/libretime/issues/2932)) ([f4b260f](https://github.com/libretime/libretime/commit/f4b260fdf70c0dd1830166d3856239dae5366599))
|
||||
* **legacy:** replay_gain_modifier should be a system preference ([#2943](https://github.com/libretime/libretime/issues/2943)) ([37d1a76](https://github.com/libretime/libretime/commit/37d1a7685e37e45734553a0eb4a4da793ca858cb))
|
||||
* remove obsolete docker compose version ([#2982](https://github.com/libretime/libretime/issues/2982)) ([fb0584b](https://github.com/libretime/libretime/commit/fb0584b021fd1c966181c7ab3989938cdfe4e642))
|
||||
* trigger legacy tasks manager every 5m ([#2987](https://github.com/libretime/libretime/issues/2987)) ([7040d0e](https://github.com/libretime/libretime/commit/7040d0e4bd92911a9072226f49ad59ce575d6ed9))
|
||||
* **worker:** ensure celery beat is started ([#3007](https://github.com/libretime/libretime/issues/3007)) ([bfde17e](https://github.com/libretime/libretime/commit/bfde17edf7fcc2bfd55263756e6ec3e455f11740))
|
||||
|
||||
## [4.0.0](https://github.com/libretime/libretime/compare/3.2.0...4.0.0) (2024-01-07)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* The media file serving is now handled by Nginx instead of the API service. The `storage.path` field is now used in the Nginx configuration, so make sure to update the Nginx configuration file if you change it.
|
||||
* **installer:** The default listen port for the installer is now `8080`. We recommend that you put a reverse proxy in front of LibreTime.
|
||||
* **installer:** The `--update-nginx` flag was removed from the installer. The nginx configuration deployed by the installer will now always be overwritten. Make sure to move your customizations to a reverse proxy configuration.
|
||||
* The default system output (`stream.outputs.system[].kind`) changed from `alsa` to `pulseaudio`. Make sure to update your configuration file if you rely on the default system output.
|
||||
* The `general.secret_key` configuration field is now required. Make sure to update your configuration file and add a secret key.
|
||||
|
||||
### Features
|
||||
|
||||
* default system output is now `pulseaudio` ([#2842](https://github.com/libretime/libretime/issues/2842)) ([083ee3f](https://github.com/libretime/libretime/commit/083ee3f1dd74441e288b4d63178ae9cea12ba286)), closes [#2542](https://github.com/libretime/libretime/issues/2542)
|
||||
* disable uvicorn worker lifespan ([#2845](https://github.com/libretime/libretime/issues/2845)) ([8743c84](https://github.com/libretime/libretime/commit/8743c84d0f007a5430e9059c197a261e613cc642))
|
||||
* **installer:** add the `--storage-path` flag ([#2865](https://github.com/libretime/libretime/issues/2865)) ([5b23852](https://github.com/libretime/libretime/commit/5b23852f8d144f0c7cdeb62831f7b1a27872b40e))
|
||||
* **installer:** change default listen port to 8080 ([#2852](https://github.com/libretime/libretime/issues/2852)) ([f72b7f9](https://github.com/libretime/libretime/commit/f72b7f9c9727800a9d77d64c540c12f272bb0ae3))
|
||||
* **installer:** remove the `--update-nginx` flag ([#2851](https://github.com/libretime/libretime/issues/2851)) ([35d7eac](https://github.com/libretime/libretime/commit/35d7eace13c2b9667fdb41fec0788118e0c5e63f))
|
||||
* **playout:** configure device for alsa and pulseaudio system outputs ([#2654](https://github.com/libretime/libretime/issues/2654)) ([06af18b](https://github.com/libretime/libretime/commit/06af18b84e7dfaad95e3b55dda22ec1ddad27050))
|
||||
* rewrite cloud-init config ([#2853](https://github.com/libretime/libretime/issues/2853)) ([8406d52](https://github.com/libretime/libretime/commit/8406d520d7a7bea4060be8a00e360bcf413cb2d5))
|
||||
* run python in optimized mode ([#2874](https://github.com/libretime/libretime/issues/2874)) ([3f7fc99](https://github.com/libretime/libretime/commit/3f7fc99b6b343fbc8df319d8130ba8247aea96d8))
|
||||
* the `general.secret_key` configuration field is now required ([#2841](https://github.com/libretime/libretime/issues/2841)) ([0d2d1a2](https://github.com/libretime/libretime/commit/0d2d1a26731a2b41ce5e574ed6de9950eaae4153)), closes [#2426](https://github.com/libretime/libretime/issues/2426)
|
||||
* use nginx to serve media files ([#2860](https://github.com/libretime/libretime/issues/2860)) ([4603c17](https://github.com/libretime/libretime/commit/4603c1759f29b8a1adb3e83d610ca00e778d76bd))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add parent function name in setValue exception ([#2777](https://github.com/libretime/libretime/issues/2777)) ([c764a5a](https://github.com/libretime/libretime/commit/c764a5a648ac6cf6c1f63cd9be6de9fe07c40988))
|
||||
* **api:** ensure non ascii paths are handled by X-Accel-Redirect ([#2861](https://github.com/libretime/libretime/issues/2861)) ([0ce63f3](https://github.com/libretime/libretime/commit/0ce63f3bf0448580170024cdde96ee351ee5c358))
|
||||
* **api:** enum schema description ([#2803](https://github.com/libretime/libretime/issues/2803)) ([976b70e](https://github.com/libretime/libretime/commit/976b70ed32a0e774cc0b72b8332372be32799ed1))
|
||||
* **api:** let nginx handle the media file content type ([#2862](https://github.com/libretime/libretime/issues/2862)) ([72268ad](https://github.com/libretime/libretime/commit/72268ad9bb1a96b24efda7143b9371d6fd98ca03))
|
||||
* **api:** move gunicorn worker config to python file ([#2854](https://github.com/libretime/libretime/issues/2854)) ([43221d9](https://github.com/libretime/libretime/commit/43221d9d7f34ba98a14db9906e350cb494a86b25))
|
||||
* **api:** paths with question marks chars are handled by X-Accel-Redirect ([#2875](https://github.com/libretime/libretime/issues/2875)) ([b2c1ceb](https://github.com/libretime/libretime/commit/b2c1ceb89fafc76f18ec650d19ec0ff03e4a20b0))
|
||||
* **deps:** update dependency friendsofphp/php-cs-fixer to <3.42.1 (main) ([#2765](https://github.com/libretime/libretime/issues/2765)) ([8ae4dce](https://github.com/libretime/libretime/commit/8ae4dce9e7c013c1f66f1b4d5da4a8c91d3419b7))
|
||||
* **deps:** update dependency friendsofphp/php-cs-fixer to <3.43.2 (main) ([#2848](https://github.com/libretime/libretime/issues/2848)) ([62e5f4d](https://github.com/libretime/libretime/commit/62e5f4dfbb76ab1919c4905570cc34274c685cef))
|
||||
* **deps:** update dependency friendsofphp/php-cs-fixer to <3.45.1 (main) ([#2855](https://github.com/libretime/libretime/issues/2855)) ([6f84328](https://github.com/libretime/libretime/commit/6f8432838058be6ef1cfa7858f17b8272929896e))
|
||||
* **deps:** update dependency friendsofphp/php-cs-fixer to <3.46.1 (main) ([#2868](https://github.com/libretime/libretime/issues/2868)) ([4827dbc](https://github.com/libretime/libretime/commit/4827dbce711262e90238bb3b6c0a35b1ce3d6877))
|
||||
* **legacy:** allow uploading opus files ([#2804](https://github.com/libretime/libretime/issues/2804)) ([f252a16](https://github.com/libretime/libretime/commit/f252a16637e113ceb1dd340fb7aad31af9c23ff0))
|
||||
* **legacy:** declare previously undeclared variable ([#2793](https://github.com/libretime/libretime/issues/2793)) ([e2cfbf4](https://github.com/libretime/libretime/commit/e2cfbf4c038f28874a206df5805f04f69a40647b))
|
||||
* **legacy:** ensure last played criteria works with never played files ([#2840](https://github.com/libretime/libretime/issues/2840)) ([24ee383](https://github.com/libretime/libretime/commit/24ee3830c23f7147f82febe3d3c6743d5ae8d4e6))
|
||||
* **playout:** increase file download chunk size to 8192 bytes ([#2863](https://github.com/libretime/libretime/issues/2863)) ([7ed1be1](https://github.com/libretime/libretime/commit/7ed1be1816abef20b9ae59a8c66a9e48a34f37c5))
|
||||
* **playout:** remove empty file when the download request failed ([#2864](https://github.com/libretime/libretime/issues/2864)) ([2facbfa](https://github.com/libretime/libretime/commit/2facbfaff23d4df0e7531b82f04f932bb2c4c9a4))
|
||||
* **worker:** unbound variable when episode url returns HTTP 404 ([#2844](https://github.com/libretime/libretime/issues/2844)) ([3f39689](https://github.com/libretime/libretime/commit/3f396895e588e62183e01d17927d9bdbea512ee0))
|
||||
|
||||
## [3.2.0](https://github.com/libretime/libretime/compare/3.1.0...3.2.0) (2023-10-16)
|
||||
|
||||
- [Release note](https://libretime.org/docs/releases/3.2.0/)
|
||||
|
||||
### Features
|
||||
|
||||
- **legacy:** move session store to database ([#2523](https://github.com/libretime/libretime/issues/2523))
|
||||
- **api:** add email configuration
|
||||
- add mobile devices stream config field ([#2744](https://github.com/libretime/libretime/issues/2744))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **playout:** liquidsoap aac output syntax errors
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.20.1 (stable) ([#2602](https://github.com/libretime/libretime/issues/2602))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.21.2 ([#2612](https://github.com/libretime/libretime/issues/2612))
|
||||
- libretime process leaks and lsof high cpu usage ([#2615](https://github.com/libretime/libretime/issues/2615))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.22.1 ([#2633](https://github.com/libretime/libretime/issues/2633))
|
||||
- libretime process leaks and lsof high cpu usage ([#2615](https://github.com/libretime/libretime/issues/2615))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.23.1 (stable) ([#2656](https://github.com/libretime/libretime/issues/2656))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.26.1 (stable) ([#2678](https://github.com/libretime/libretime/issues/2678))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.26.1 (main) ([#2677](https://github.com/libretime/libretime/issues/2677))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.26.2 ([#2686](https://github.com/libretime/libretime/issues/2686))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.26.2 ([#2687](https://github.com/libretime/libretime/issues/2687))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.27.1 (main) ([#2714](https://github.com/libretime/libretime/issues/2714))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.27.1 (stable) ([#2715](https://github.com/libretime/libretime/issues/2715))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.34.1 ([#2723](https://github.com/libretime/libretime/issues/2723))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.35.2 ([#2738](https://github.com/libretime/libretime/issues/2738))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.35.2 ([#2722](https://github.com/libretime/libretime/issues/2722))
|
||||
|
||||
### Documentation
|
||||
|
||||
- update chat links to point to matrix ([#2571](https://github.com/libretime/libretime/issues/2571))
|
||||
- fix broken link ([#2616](https://github.com/libretime/libretime/issues/2616))
|
||||
|
||||
### Tests
|
||||
|
||||
- **playout:** check unsupported liquidsoap aac output
|
||||
<a name="3.1.0"></a>
|
||||
|
||||
## [3.1.0](https://github.com/libretime/libretime/compare/3.0.2...3.1.0) (2023-05-26)
|
||||
|
||||
|
@ -326,6 +190,8 @@
|
|||
|
||||
- chore(api): install django-rest-framework from git ([#2518](https://github.com/libretime/libretime/issues/2518))
|
||||
|
||||
<a name="3.0.2"></a>
|
||||
|
||||
## [3.0.2](https://github.com/libretime/libretime/compare/3.0.1...3.0.2) (2023-02-21)
|
||||
|
||||
- [Release note](https://libretime.org/docs/releases/3.0.2/)
|
||||
|
@ -357,6 +223,8 @@
|
|||
- don't squash commits during docs sync
|
||||
- test project weekly
|
||||
|
||||
<a name="3.0.1"></a>
|
||||
|
||||
## [3.0.1](https://github.com/libretime/libretime/compare/3.0.0...3.0.1) (2022-12-20)
|
||||
|
||||
- [Release note](https://libretime.org/docs/releases/3.0.1/)
|
||||
|
@ -385,6 +253,8 @@
|
|||
- sync docs with libretime/website repository
|
||||
- pin vale version to v2.21.3
|
||||
|
||||
<a name="3.0.0"></a>
|
||||
|
||||
## [3.0.0](https://github.com/libretime/libretime/compare/3.0.0-beta.2...3.0.0) (2022-10-10)
|
||||
|
||||
- [Release note](https://libretime.org/docs/releases/3.0.0/)
|
||||
|
@ -405,6 +275,8 @@
|
|||
|
||||
- **analyzer:** fix wrong bit_rate values
|
||||
|
||||
<a name="3.0.0-beta.2"></a>
|
||||
|
||||
## [3.0.0-beta.2](https://github.com/libretime/libretime/compare/3.0.0-beta.1...3.0.0-beta.2) (2022-10-03)
|
||||
|
||||
- [Release note](https://libretime.org/docs/releases/3.0.0-beta.2/)
|
||||
|
@ -433,6 +305,8 @@
|
|||
- allow failure when linting /docs/releases
|
||||
- use github.ref_name to get tag
|
||||
|
||||
<a name="3.0.0-beta.1"></a>
|
||||
|
||||
## [3.0.0-beta.1](https://github.com/libretime/libretime/compare/3.0.0-beta.0...3.0.0-beta.1) (2022-09-23)
|
||||
|
||||
- [Release note](https://libretime.org/docs/releases/3.0.0-beta.1/)
|
||||
|
@ -465,6 +339,8 @@
|
|||
- don't check github.com/libretime/libretime/(issues|pulls) links
|
||||
- run docs workflow on vale files changes
|
||||
|
||||
<a name="3.0.0-beta.0"></a>
|
||||
|
||||
## [3.0.0-beta.0](https://github.com/libretime/libretime/compare/3.0.0-alpha.13...3.0.0-beta.0) (2022-09-16)
|
||||
|
||||
- [Release note](https://libretime.org/docs/releases/3.0.0-beta.0/)
|
||||
|
@ -614,6 +490,8 @@
|
|||
- improve containers build caching
|
||||
- add container tags
|
||||
|
||||
<a name="3.0.0-alpha.13"></a>
|
||||
|
||||
## [3.0.0-alpha.13](https://github.com/libretime/libretime/compare/3.0.0-alpha.12...3.0.0-alpha.13) (2022-07-15)
|
||||
|
||||
- [Release note](https://libretime.org/docs/releases/3.0.0-alpha.13/)
|
||||
|
@ -774,6 +652,8 @@
|
|||
- disable codecov project status check
|
||||
- disable codecov patch status check
|
||||
|
||||
<a name="3.0.0-alpha.12"></a>
|
||||
|
||||
## [3.0.0-alpha.12](https://github.com/libretime/libretime/compare/3.0.0-alpha.11...3.0.0-alpha.12) (2022-03-29)
|
||||
|
||||
- [Release note](https://libretime.org/docs/releases/3.0.0-alpha.12/)
|
||||
|
@ -788,6 +668,8 @@
|
|||
- add missing data to release note
|
||||
- fix and update links ([#1714](https://github.com/libretime/libretime/issues/1714))
|
||||
|
||||
<a name="3.0.0-alpha.11"></a>
|
||||
|
||||
## [3.0.0-alpha.11](https://github.com/libretime/libretime/compare/3.0.0-alpha.10...3.0.0-alpha.11) (2022-03-28)
|
||||
|
||||
- [Release note](https://libretime.org/docs/releases/3.0.0-alpha.11/)
|
||||
|
|
31
Dockerfile
31
Dockerfile
|
@ -2,7 +2,7 @@ ARG LIBRETIME_VERSION
|
|||
#======================================================================================#
|
||||
# Python Builder #
|
||||
#======================================================================================#
|
||||
FROM python:3.10-slim-bullseye AS python-builder
|
||||
FROM python:3.10-slim-bullseye as python-builder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
|
@ -18,7 +18,7 @@ RUN pip wheel --wheel-dir . --no-deps .
|
|||
#======================================================================================#
|
||||
# Python base #
|
||||
#======================================================================================#
|
||||
FROM python:3.10-slim-bullseye AS python-base
|
||||
FROM python:3.10-slim-bullseye as python-base
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
@ -48,7 +48,7 @@ RUN set -eux \
|
|||
#======================================================================================#
|
||||
# Python base with ffmpeg #
|
||||
#======================================================================================#
|
||||
FROM python-base AS python-base-ffmpeg
|
||||
FROM python-base as python-base-ffmpeg
|
||||
|
||||
RUN set -eux \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get update \
|
||||
|
@ -59,7 +59,7 @@ RUN set -eux \
|
|||
#======================================================================================#
|
||||
# Analyzer #
|
||||
#======================================================================================#
|
||||
FROM python-base-ffmpeg AS libretime-analyzer
|
||||
FROM python-base-ffmpeg as libretime-analyzer
|
||||
|
||||
COPY tools/packages.py /tmp/packages.py
|
||||
COPY analyzer/packages.ini /tmp/packages.ini
|
||||
|
@ -97,7 +97,7 @@ ENV LIBRETIME_VERSION=$LIBRETIME_VERSION
|
|||
#======================================================================================#
|
||||
# Playout #
|
||||
#======================================================================================#
|
||||
FROM python-base-ffmpeg AS libretime-playout
|
||||
FROM python-base-ffmpeg as libretime-playout
|
||||
|
||||
COPY tools/packages.py /tmp/packages.py
|
||||
COPY playout/packages.ini /tmp/packages.ini
|
||||
|
@ -136,12 +136,11 @@ ENV LIBRETIME_VERSION=$LIBRETIME_VERSION
|
|||
#======================================================================================#
|
||||
# API #
|
||||
#======================================================================================#
|
||||
FROM python-base AS libretime-api
|
||||
FROM python-base as libretime-api
|
||||
|
||||
RUN set -eux \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
gcc \
|
||||
libc6-dev \
|
||||
libpq-dev \
|
||||
|
@ -167,7 +166,7 @@ WORKDIR /app
|
|||
|
||||
CMD ["/usr/local/bin/gunicorn", \
|
||||
"--workers=4", \
|
||||
"--worker-class=libretime_api.gunicorn.Worker", \
|
||||
"--worker-class=uvicorn.workers.UvicornWorker", \
|
||||
"--log-file", "-", \
|
||||
"--bind=0.0.0.0:9001", \
|
||||
"libretime_api.asgi"]
|
||||
|
@ -175,12 +174,13 @@ CMD ["/usr/local/bin/gunicorn", \
|
|||
ARG LIBRETIME_VERSION
|
||||
ENV LIBRETIME_VERSION=$LIBRETIME_VERSION
|
||||
|
||||
HEALTHCHECK CMD ["curl", "--fail", "http://localhost:9001/api/v2/version"]
|
||||
HEALTHCHECK CMD ["python3", "-c", \
|
||||
"import requests; requests.get('http://localhost:9001/api/v2/version').raise_for_status()"]
|
||||
|
||||
#======================================================================================#
|
||||
# Worker #
|
||||
#======================================================================================#
|
||||
FROM python-base AS libretime-worker
|
||||
FROM python-base as libretime-worker
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
|
@ -189,7 +189,6 @@ RUN --mount=type=cache,target=/root/.cache/pip \
|
|||
pip install --no-compile -r requirements.txt
|
||||
|
||||
COPY --from=python-builder /build/shared/*.whl .
|
||||
COPY --from=python-builder /build/api-client/*.whl .
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
pip install --no-compile *.whl && rm -Rf *.whl
|
||||
|
||||
|
@ -201,14 +200,20 @@ RUN --mount=type=cache,target=/root/.cache/pip \
|
|||
USER ${UID}:${GID}
|
||||
WORKDIR /app
|
||||
|
||||
CMD ["/usr/local/bin/libretime-worker"]
|
||||
CMD ["/usr/local/bin/celery", "worker", \
|
||||
"--app=libretime_worker.tasks:worker", \
|
||||
"--config=libretime_worker.config", \
|
||||
"--time-limit=1800", \
|
||||
"--concurrency=1", \
|
||||
"--loglevel=info"]
|
||||
|
||||
ARG LIBRETIME_VERSION
|
||||
ENV LIBRETIME_VERSION=$LIBRETIME_VERSION
|
||||
|
||||
#======================================================================================#
|
||||
# Legacy #
|
||||
#======================================================================================#
|
||||
FROM php:7.4-fpm AS libretime-legacy
|
||||
FROM php:7.4-fpm as libretime-legacy
|
||||
|
||||
ENV LIBRETIME_CONFIG_FILEPATH=/etc/libretime/config.yml
|
||||
ENV LIBRETIME_LOG_FILEPATH=php://stderr
|
||||
|
|
11
Makefile
11
Makefile
|
@ -22,15 +22,18 @@ dev-certs:
|
|||
cat dev/certs/fake.{key,crt} > dev/certs/fake.pem
|
||||
|
||||
dev: .env dev-certs
|
||||
DOCKER_BUILDKIT=1 docker compose build
|
||||
docker compose run --rm legacy make build
|
||||
docker compose run --rm api libretime-api migrate
|
||||
docker compose up -d
|
||||
DOCKER_BUILDKIT=1 docker-compose build
|
||||
docker-compose run --rm legacy make build
|
||||
docker-compose run --rm api libretime-api migrate
|
||||
docker-compose up -d
|
||||
|
||||
.PHONY: VERSION
|
||||
VERSION:
|
||||
tools/version.sh
|
||||
|
||||
changelog:
|
||||
tools/changelog.sh
|
||||
|
||||
.PHONY: tarball
|
||||
tarball: VERSION
|
||||
$(MAKE) -C legacy build
|
||||
|
|
19
README.md
19
README.md
|
@ -52,8 +52,23 @@ Become a financial contributor and help us sustain our community on
|
|||
[Support](https://opencollective.com/libretime/contribute) this project with
|
||||
your organization. Your logo will show up here with a link to your website.
|
||||
|
||||
<a href="https://opencollective.com/libretime">
|
||||
<img src="https://opencollective.com/libretime/organizations.svg?width=890">
|
||||
<a href="https://opencollective.com/libretime/organization/0/website">
|
||||
<img src="https://opencollective.com/libretime/organization/0/avatar.svg">
|
||||
</a>
|
||||
<a href="https://opencollective.com/libretime/organization/1/website">
|
||||
<img src="https://opencollective.com/libretime/organization/1/avatar.svg">
|
||||
</a>
|
||||
<a href="https://opencollective.com/libretime/organization/2/website">
|
||||
<img src="https://opencollective.com/libretime/organization/2/avatar.svg">
|
||||
</a>
|
||||
<a href="https://opencollective.com/libretime/organization/3/website">
|
||||
<img src="https://opencollective.com/libretime/organization/3/avatar.svg">
|
||||
</a>
|
||||
<a href="https://opencollective.com/libretime/organization/4/website">
|
||||
<img src="https://opencollective.com/libretime/organization/4/avatar.svg">
|
||||
</a>
|
||||
<a href="https://opencollective.com/libretime/organization/5/website">
|
||||
<img src="https://opencollective.com/libretime/organization/5/avatar.svg">
|
||||
</a>
|
||||
|
||||
## License
|
||||
|
|
|
@ -79,6 +79,7 @@ Vagrant.configure('2') do |config|
|
|||
LIBRETIME_POSTGRESQL_PASSWORD=libretime \
|
||||
LIBRETIME_RABBITMQ_PASSWORD=libretime \
|
||||
bash install \
|
||||
--listen-port 8080 \
|
||||
--in-place \
|
||||
http://192.168.10.100:8080
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ ProtectKernelTunables=true
|
|||
ProtectProc=invisible
|
||||
ProtectSystem=full
|
||||
|
||||
Environment=PYTHONOPTIMIZE=2
|
||||
Environment=LIBRETIME_CONFIG_FILEPATH=@@CONFIG_FILEPATH@@
|
||||
Environment=LIBRETIME_LOG_FILEPATH=@@LOG_DIR@@/analyzer.log
|
||||
WorkingDirectory=@@WORKING_DIR@@/analyzer
|
||||
|
|
|
@ -36,7 +36,7 @@ def probe_replaygain(filepath: Path) -> Optional[float]:
|
|||
"""
|
||||
Probe replaygain will probe the given audio file and return the replaygain if available.
|
||||
"""
|
||||
cmd = _ffprobe("-i", filepath, errors="backslashreplace")
|
||||
cmd = _ffprobe("-i", filepath)
|
||||
|
||||
track_gain_match = _PROBE_REPLAYGAIN_RE.search(cmd.stderr)
|
||||
|
||||
|
@ -75,7 +75,8 @@ def compute_silences(filepath: Path) -> List[Tuple[float, float]]:
|
|||
cmd = _ffmpeg(
|
||||
*("-i", filepath),
|
||||
"-vn",
|
||||
*("-filter", "highpass=frequency=80,silencedetect=noise=-60dB:duration=0.9"),
|
||||
*("-filter", "highpass=frequency=1000"),
|
||||
*("-filter", "silencedetect=noise=0.15:duration=1"),
|
||||
)
|
||||
|
||||
starts, ends = [], []
|
||||
|
|
|
@ -5,24 +5,10 @@ from typing import Any, Dict
|
|||
|
||||
import mutagen
|
||||
from libretime_shared.files import compute_md5
|
||||
from mutagen.easyid3 import EasyID3
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def flatten(xss):
|
||||
return [x for xs in xss for x in xs]
|
||||
|
||||
|
||||
def comment_get(id3, _):
|
||||
comments = [v.text for k, v in id3.items() if "COMM" in k or "comment" in k]
|
||||
|
||||
return flatten(comments)
|
||||
|
||||
|
||||
EasyID3.RegisterKey("comment", comment_get)
|
||||
|
||||
|
||||
def analyze_metadata(filepath_: str, metadata: Dict[str, Any]):
|
||||
"""
|
||||
Extract audio metadata from tags embedded in the file using mutagen.
|
||||
|
@ -85,36 +71,34 @@ def analyze_metadata(filepath_: str, metadata: Dict[str, Any]):
|
|||
except (AttributeError, KeyError, IndexError):
|
||||
pass
|
||||
|
||||
extracted_tags_mapping = [
|
||||
("title", "track_title"),
|
||||
("artist", "artist_name"),
|
||||
("album", "album_title"),
|
||||
("bpm", "bpm"),
|
||||
("composer", "composer"),
|
||||
("conductor", "conductor"),
|
||||
("copyright", "copyright"),
|
||||
("comment", "comment"),
|
||||
("comment", "comments"),
|
||||
("comment", "description"),
|
||||
("encoded_by", "encoder"),
|
||||
("genre", "genre"),
|
||||
("isrc", "isrc"),
|
||||
("label", "label"),
|
||||
("organization", "label"),
|
||||
# ("length", "length"),
|
||||
("language", "language"),
|
||||
("last_modified", "last_modified"),
|
||||
("mood", "mood"),
|
||||
("bit_rate", "bit_rate"),
|
||||
("replay_gain", "replaygain"),
|
||||
# ("tracknumber", "track_number"),
|
||||
# ("track_total", "track_total"),
|
||||
("website", "website"),
|
||||
("date", "year"),
|
||||
# ("mime_type", "mime"),
|
||||
]
|
||||
extracted_tags_mapping = {
|
||||
"title": "track_title",
|
||||
"artist": "artist_name",
|
||||
"album": "album_title",
|
||||
"bpm": "bpm",
|
||||
"composer": "composer",
|
||||
"conductor": "conductor",
|
||||
"copyright": "copyright",
|
||||
"comment": "comment",
|
||||
"encoded_by": "encoder",
|
||||
"genre": "genre",
|
||||
"isrc": "isrc",
|
||||
"label": "label",
|
||||
"organization": "label",
|
||||
# "length": "length",
|
||||
"language": "language",
|
||||
"last_modified": "last_modified",
|
||||
"mood": "mood",
|
||||
"bit_rate": "bit_rate",
|
||||
"replay_gain": "replaygain",
|
||||
# "tracknumber": "track_number",
|
||||
# "track_total": "track_total",
|
||||
"website": "website",
|
||||
"date": "year",
|
||||
# "mime_type": "mime",
|
||||
}
|
||||
|
||||
for extracted_key, metadata_key in extracted_tags_mapping:
|
||||
for extracted_key, metadata_key in extracted_tags_mapping.items():
|
||||
try:
|
||||
metadata[metadata_key] = extracted[extracted_key]
|
||||
if isinstance(metadata[metadata_key], list):
|
||||
|
|
|
@ -16,7 +16,8 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
class Step(Protocol):
|
||||
@staticmethod
|
||||
def __call__(filename: str, metadata: Dict[str, Any]): ...
|
||||
def __call__(filename: str, metadata: Dict[str, Any]):
|
||||
...
|
||||
|
||||
|
||||
class PipelineStatus(int, Enum):
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
# This file is auto-generated by tools/extract_requirements.py.
|
||||
mutagen>=1.45.1,<1.48
|
||||
pika>=1.0.0,<1.4
|
||||
requests>=2.32.2,<2.33
|
||||
requests>=2.31.0,<2.32
|
||||
typing_extensions
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
from setuptools import find_packages, setup
|
||||
|
||||
version = "4.2.0" # x-release-please-version
|
||||
|
||||
setup(
|
||||
name="libretime-analyzer",
|
||||
version=version,
|
||||
version="3.2.0",
|
||||
description="Libretime Analyzer",
|
||||
author="LibreTime Contributors",
|
||||
url="https://github.com/libretime/libretime",
|
||||
|
@ -24,7 +22,7 @@ setup(
|
|||
install_requires=[
|
||||
"mutagen>=1.45.1,<1.48",
|
||||
"pika>=1.0.0,<1.4",
|
||||
"requests>=2.32.2,<2.33",
|
||||
"requests>=2.31.0,<2.32",
|
||||
"typing_extensions",
|
||||
],
|
||||
extras_require={
|
||||
|
|
|
@ -23,28 +23,28 @@ FILES = [
|
|||
# 8s -> 9s: silence
|
||||
# 9s -> 12s: musik
|
||||
# 12s -> 15s: pink noise fade out
|
||||
Fixture(here / "s1-jointstereo.mp3", 15.0, 1.4, 15.0, -5.9 ),
|
||||
Fixture(here / "s1-mono.mp3", 15.0, 1.5, 15.0, -2.0 ),
|
||||
Fixture(here / "s1-stereo.mp3", 15.0, 1.4, 15.0, -5.9 ),
|
||||
Fixture(here / "s1-mono-12.mp3", 15.0, 1.2, 15.0, +7.0 ),
|
||||
Fixture(here / "s1-stereo-12.mp3", 15.0, 1.2, 15.0, +6.1 ),
|
||||
Fixture(here / "s1-mono+12.mp3", 15.0, 1.2, 15.0, -17.0 ),
|
||||
Fixture(here / "s1-stereo+12.mp3", 15.0, 1.2, 15.0, -17.8 ),
|
||||
Fixture(here / "s1-mono.flac", 15.0, 1.4, 15.0, -2.3 ),
|
||||
Fixture(here / "s1-stereo.flac", 15.0, 1.4, 15.0, -6.0 ),
|
||||
Fixture(here / "s1-mono-12.flac", 15.0, 2.0, 15.0, +10.0 ),
|
||||
Fixture(here / "s1-stereo-12.flac", 15.0, 1.8, 15.0, +5.9 ),
|
||||
Fixture(here / "s1-mono+12.flac", 15.0, 0.0, 15.0, -12.0 ),
|
||||
Fixture(here / "s1-stereo+12.flac", 15.0, 0.0, 15.0, -14.9 ),
|
||||
Fixture(here / "s1-mono.m4a", 15.0, 1.4, 15.0, -4.5 ),
|
||||
Fixture(here / "s1-stereo.m4a", 15.0, 1.4, 15.0, -5.8 ),
|
||||
Fixture(here / "s1-mono.ogg", 15.0, 1.4, 15.0, -4.9 ),
|
||||
Fixture(here / "s1-stereo.ogg", 15.0, 1.4, 15.0, -5.7 ),
|
||||
Fixture(here / "s1-stereo", 15.0, 1.4, 15.0, -5.7 ),
|
||||
Fixture(here / "s1-mono.wav", 15.0, 1.5, 15.0, -2.3 ),
|
||||
Fixture(here / "s1-stereo.wav", 15.0, 1.4, 15.0, -6.0 ),
|
||||
Fixture(here / "s1-jointstereo.mp3", 15.0, 6.0, 13.0, -5.9 ),
|
||||
Fixture(here / "s1-mono.mp3", 15.0, 6.0, 13.0, -2.0 ),
|
||||
Fixture(here / "s1-stereo.mp3", 15.0, 6.0, 13.0, -5.9 ),
|
||||
Fixture(here / "s1-mono-12.mp3", 15.0, 9.0, 12.0, +7.0 ),
|
||||
Fixture(here / "s1-stereo-12.mp3", 15.0, 9.0, 12.0, +6.1 ),
|
||||
Fixture(here / "s1-mono+12.mp3", 15.0, 3.5, 13.0, -17.0 ),
|
||||
Fixture(here / "s1-stereo+12.mp3", 15.0, 3.5, 13.0, -17.8 ),
|
||||
Fixture(here / "s1-mono.flac", 15.0, 6.0, 13.0, -2.3 ),
|
||||
Fixture(here / "s1-stereo.flac", 15.0, 6.0, 13.0, -6.0 ),
|
||||
Fixture(here / "s1-mono-12.flac", 15.0, 9.0, 12.0, +10.0 ),
|
||||
Fixture(here / "s1-stereo-12.flac", 15.0, 9.0, 12.0, +5.9 ),
|
||||
Fixture(here / "s1-mono+12.flac", 15.0, 3.5, 13.0, -12.0 ),
|
||||
Fixture(here / "s1-stereo+12.flac", 15.0, 3.5, 13.0, -14.9 ),
|
||||
Fixture(here / "s1-mono.m4a", 15.0, 6.0, 13.0, -4.5 ),
|
||||
Fixture(here / "s1-stereo.m4a", 15.0, 6.0, 13.0, -5.8 ),
|
||||
Fixture(here / "s1-mono.ogg", 15.0, 6.0, 13.0, -4.9 ),
|
||||
Fixture(here / "s1-stereo.ogg", 15.0, 6.0, 13.0, -5.7 ),
|
||||
Fixture(here / "s1-stereo", 15.0, 6.0, 13.0, -5.7 ),
|
||||
Fixture(here / "s1-mono.wav", 15.0, 6.0, 13.0, -2.3 ),
|
||||
Fixture(here / "s1-stereo.wav", 15.0, 6.0, 13.0, -6.0 ),
|
||||
# sample 1 large (looped for 2 hours)
|
||||
Fixture(here / "s1-large.flac", 7200, 1.4, 7200, -6.0 ),
|
||||
Fixture(here / "s1-large.flac", 7200, 6.0, 7198, -6.0 ),
|
||||
# sample 2
|
||||
# 0s -> 1.8s: silence
|
||||
# 1.8s : noise
|
||||
|
@ -96,18 +96,12 @@ tags = {
|
|||
"comment": "Test Comment",
|
||||
}
|
||||
|
||||
mp3Tags = {
|
||||
**tags,
|
||||
"comments": tags["comment"],
|
||||
"description": tags["comment"],
|
||||
}
|
||||
|
||||
FILES_TAGGED = [
|
||||
FixtureMeta(
|
||||
here / "s1-jointstereo-tagged.mp3",
|
||||
{
|
||||
**meta,
|
||||
**mp3Tags,
|
||||
**tags,
|
||||
"bit_rate": approx(128000, abs=1e2),
|
||||
"channels": 2,
|
||||
"mime": "audio/mp3",
|
||||
|
@ -117,7 +111,7 @@ FILES_TAGGED = [
|
|||
here / "s1-mono-tagged.mp3",
|
||||
{
|
||||
**meta,
|
||||
**mp3Tags,
|
||||
**tags,
|
||||
"bit_rate": approx(64000, abs=1e2),
|
||||
"channels": 1,
|
||||
"mime": "audio/mp3",
|
||||
|
@ -127,7 +121,7 @@ FILES_TAGGED = [
|
|||
here / "s1-stereo-tagged.mp3",
|
||||
{
|
||||
**meta,
|
||||
**mp3Tags,
|
||||
**tags,
|
||||
"bit_rate": approx(128000, abs=1e2),
|
||||
"channels": 2,
|
||||
"mime": "audio/mp3",
|
||||
|
@ -157,7 +151,7 @@ FILES_TAGGED = [
|
|||
here / "s1-mono-tagged.m4a",
|
||||
{
|
||||
**meta,
|
||||
**mp3Tags,
|
||||
**tags,
|
||||
"bit_rate": approx(65000, abs=5e4),
|
||||
"channels": 2, # Weird
|
||||
"mime": "audio/mp4",
|
||||
|
@ -167,7 +161,7 @@ FILES_TAGGED = [
|
|||
here / "s1-stereo-tagged.m4a",
|
||||
{
|
||||
**meta,
|
||||
**mp3Tags,
|
||||
**tags,
|
||||
"bit_rate": approx(128000, abs=1e5),
|
||||
"channels": 2,
|
||||
"mime": "audio/mp4",
|
||||
|
@ -234,18 +228,12 @@ tags = {
|
|||
"comment": "Ł Ą Ż Ę Ć Ń Ś Ź",
|
||||
}
|
||||
|
||||
mp3Tags = {
|
||||
**tags,
|
||||
"comments": tags["comment"],
|
||||
"description": tags["comment"],
|
||||
}
|
||||
|
||||
FILES_TAGGED += [
|
||||
FixtureMeta(
|
||||
here / "s1-jointstereo-tagged-utf8.mp3",
|
||||
{
|
||||
**meta,
|
||||
**mp3Tags,
|
||||
**tags,
|
||||
"bit_rate": approx(128000, abs=1e2),
|
||||
"channels": 2,
|
||||
"mime": "audio/mp3",
|
||||
|
@ -255,7 +243,7 @@ FILES_TAGGED += [
|
|||
here / "s1-mono-tagged-utf8.mp3",
|
||||
{
|
||||
**meta,
|
||||
**mp3Tags,
|
||||
**tags,
|
||||
"bit_rate": approx(64000, abs=1e2),
|
||||
"channels": 1,
|
||||
"mime": "audio/mp3",
|
||||
|
@ -265,7 +253,7 @@ FILES_TAGGED += [
|
|||
here / "s1-stereo-tagged-utf8.mp3",
|
||||
{
|
||||
**meta,
|
||||
**mp3Tags,
|
||||
**tags,
|
||||
"bit_rate": approx(128000, abs=1e2),
|
||||
"channels": 2,
|
||||
"mime": "audio/mp3",
|
||||
|
@ -295,7 +283,7 @@ FILES_TAGGED += [
|
|||
here / "s1-mono-tagged-utf8.m4a",
|
||||
{
|
||||
**meta,
|
||||
**mp3Tags,
|
||||
**tags,
|
||||
"bit_rate": approx(65000, abs=5e4),
|
||||
"channels": 2, # Weird
|
||||
"mime": "audio/mp4",
|
||||
|
@ -305,7 +293,7 @@ FILES_TAGGED += [
|
|||
here / "s1-stereo-tagged-utf8.m4a",
|
||||
{
|
||||
**meta,
|
||||
**mp3Tags,
|
||||
**tags,
|
||||
"bit_rate": approx(128000, abs=1e5),
|
||||
"channels": 2,
|
||||
"mime": "audio/mp4",
|
||||
|
|
|
@ -27,8 +27,8 @@ def test_analyze_metadata(filepath: Path, metadata: dict):
|
|||
del metadata["length"]
|
||||
del found["length"]
|
||||
|
||||
# ogg,flac files does not support comments yet
|
||||
if not filepath.suffix == ".m4a" and not filepath.suffix == ".mp3":
|
||||
# mp3,ogg,flac files does not support comments yet
|
||||
if not filepath.suffix == ".m4a":
|
||||
if "comment" in metadata:
|
||||
del metadata["comment"]
|
||||
|
||||
|
|
|
@ -214,6 +214,3 @@ class ApiClient:
|
|||
|
||||
def update_metadata_on_tunein(self):
|
||||
self._base_client.update_metadata_on_tunein()
|
||||
|
||||
def trigger_task_manager(self):
|
||||
self._base_client.version()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Please do not edit this file, edit the setup.py file!
|
||||
# This file is auto-generated by tools/extract_requirements.py.
|
||||
python-dateutil>=2.8.1,<2.10
|
||||
requests>=2.32.2,<2.33
|
||||
python-dateutil>=2.8.1,<2.9
|
||||
requests>=2.31.0,<2.32
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
from setuptools import find_packages, setup
|
||||
|
||||
version = "4.2.0" # x-release-please-version
|
||||
|
||||
setup(
|
||||
name="libretime-api-client",
|
||||
version=version,
|
||||
version="3.2.0",
|
||||
description="LibreTime API Client",
|
||||
author="LibreTime Contributors",
|
||||
url="https://github.com/libretime/libretime",
|
||||
|
@ -18,8 +16,8 @@ setup(
|
|||
package_data={"": ["py.typed"]},
|
||||
python_requires=">=3.8",
|
||||
install_requires=[
|
||||
"python-dateutil>=2.8.1,<2.10",
|
||||
"requests>=2.32.2,<2.33",
|
||||
"python-dateutil>=2.8.1,<2.9",
|
||||
"requests>=2.31.0,<2.32",
|
||||
],
|
||||
extras_require={
|
||||
"dev": [
|
||||
|
|
|
@ -18,7 +18,6 @@ ProtectKernelTunables=true
|
|||
ProtectProc=invisible
|
||||
ProtectSystem=full
|
||||
|
||||
Environment=PYTHONOPTIMIZE=2
|
||||
Environment=LIBRETIME_CONFIG_FILEPATH=@@CONFIG_FILEPATH@@
|
||||
Environment=LIBRETIME_LOG_FILEPATH=@@LOG_DIR@@/api.log
|
||||
|
||||
|
@ -26,7 +25,7 @@ Type=notify
|
|||
KillMode=mixed
|
||||
ExecStart=@@VENV_DIR@@/bin/gunicorn \
|
||||
--workers 4 \
|
||||
--worker-class libretime_api.gunicorn.Worker \
|
||||
--worker-class uvicorn.workers.UvicornWorker \
|
||||
--log-file - \
|
||||
--bind unix:/run/libretime-api.sock \
|
||||
libretime_api.asgi
|
||||
|
|
|
@ -18,13 +18,12 @@ class StreamPreferences(BaseModel):
|
|||
input_fade_transition: float
|
||||
message_format: MessageFormatKind
|
||||
message_offline: str
|
||||
replay_gain_enabled: bool
|
||||
replay_gain_offset: float
|
||||
|
||||
# input_auto_switch_off: bool
|
||||
# input_auto_switch_on: bool
|
||||
# input_main_user: str
|
||||
# input_main_password: str
|
||||
# replay_gain_enabled: bool
|
||||
# replay_gain_offset: float
|
||||
# track_fade_in: float
|
||||
# track_fade_out: float
|
||||
# track_fade_transition: float
|
||||
|
@ -83,8 +82,6 @@ class Preference(models.Model):
|
|||
int(entries.get("stream_label_format") or 0)
|
||||
),
|
||||
message_offline=entries.get("off_air_meta") or "Offline",
|
||||
replay_gain_enabled=entries.get("enable_replay_gain") == "1",
|
||||
replay_gain_offset=float(entries.get("replay_gain_modifier") or 0.0),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -8,7 +8,6 @@ from .role import Role
|
|||
|
||||
|
||||
class UserManager(BaseUserManager):
|
||||
# pylint: disable=too-many-positional-arguments
|
||||
def create_user(self, role, username, password, email, first_name, last_name):
|
||||
user = self.model(
|
||||
role=role,
|
||||
|
@ -21,7 +20,6 @@ class UserManager(BaseUserManager):
|
|||
user.save(using=self._db)
|
||||
return user
|
||||
|
||||
# pylint: disable=too-many-positional-arguments
|
||||
def create_superuser(self, username, password, email, first_name, last_name):
|
||||
return self.create_user(
|
||||
Role.ADMIN,
|
||||
|
|
|
@ -6,8 +6,6 @@ class StreamPreferencesSerializer(serializers.Serializer):
|
|||
input_fade_transition = serializers.FloatField(read_only=True)
|
||||
message_format = serializers.IntegerField(read_only=True)
|
||||
message_offline = serializers.CharField(read_only=True)
|
||||
replay_gain_enabled = serializers.BooleanField(read_only=True)
|
||||
replay_gain_offset = serializers.FloatField(read_only=True)
|
||||
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
|
|
|
@ -4,7 +4,7 @@ from libretime_api.core.models.preference import Preference
|
|||
# pylint: disable=invalid-name,unused-argument
|
||||
def test_preference_get_site_preferences(db):
|
||||
result = Preference.get_site_preferences()
|
||||
assert result.model_dump() == {
|
||||
assert result.dict() == {
|
||||
"station_name": "LibreTime",
|
||||
}
|
||||
|
||||
|
@ -12,19 +12,17 @@ def test_preference_get_site_preferences(db):
|
|||
# pylint: disable=invalid-name,unused-argument
|
||||
def test_preference_get_stream_preferences(db):
|
||||
result = Preference.get_stream_preferences()
|
||||
assert result.model_dump() == {
|
||||
assert result.dict() == {
|
||||
"input_fade_transition": 0.0,
|
||||
"message_format": 0,
|
||||
"message_offline": "LibreTime - offline",
|
||||
"replay_gain_enabled": True,
|
||||
"replay_gain_offset": 0.0,
|
||||
}
|
||||
|
||||
|
||||
# pylint: disable=invalid-name,unused-argument
|
||||
def test_preference_get_stream_state(db):
|
||||
result = Preference.get_stream_state()
|
||||
assert result.model_dump() == {
|
||||
assert result.dict() == {
|
||||
"input_main_connected": False,
|
||||
"input_main_streaming": False,
|
||||
"input_show_connected": False,
|
||||
|
|
|
@ -9,8 +9,6 @@ def test_stream_preferences_get(db, api_client: APIClient):
|
|||
"input_fade_transition": 0.0,
|
||||
"message_format": 0,
|
||||
"message_offline": "LibreTime - offline",
|
||||
"replay_gain_enabled": True,
|
||||
"replay_gain_offset": 0.0,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ class InfoView(APIView):
|
|||
def get(self, request):
|
||||
data = Preference.get_site_preferences()
|
||||
return Response(
|
||||
data.model_dump(
|
||||
data.dict(
|
||||
include={
|
||||
"station_name",
|
||||
}
|
||||
|
|
|
@ -14,13 +14,11 @@ class StreamPreferencesView(views.APIView):
|
|||
def get(self, request):
|
||||
data = Preference.get_stream_preferences()
|
||||
return Response(
|
||||
data.model_dump(
|
||||
data.dict(
|
||||
include={
|
||||
"input_fade_transition",
|
||||
"message_format",
|
||||
"message_offline",
|
||||
"replay_gain_enabled",
|
||||
"replay_gain_offset",
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -34,7 +32,7 @@ class StreamStateView(views.APIView):
|
|||
def get(self, request):
|
||||
data = Preference.get_stream_state()
|
||||
return Response(
|
||||
data.model_dump(
|
||||
data.dict(
|
||||
include={
|
||||
"input_main_connected",
|
||||
"input_main_streaming",
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
from uvicorn.workers import UvicornWorker # pylint: disable=import-error
|
||||
|
||||
|
||||
class Worker(UvicornWorker):
|
||||
CONFIG_KWARGS = {"lifespan": "off"}
|
|
@ -8,6 +8,8 @@ UP = """
|
|||
-- DELETE FROM cc_pref WHERE keystr = 'system_version';
|
||||
-- INSERT INTO cc_pref (keystr, valstr) VALUES ('system_version', '2.5.5');
|
||||
|
||||
ALTER TABLE cc_show ADD COLUMN image_path varchar(255) DEFAULT '';
|
||||
ALTER TABLE cc_show_instances ADD COLUMN description varchar(255) DEFAULT '';
|
||||
"""
|
||||
|
||||
DOWN = None
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.db import migrations
|
|||
from ._migrations import legacy_migration_factory
|
||||
|
||||
UP = """
|
||||
ALTER TABLE cc_files ADD COLUMN artwork VARCHAR(255);
|
||||
ALTER TABLE cc_files ADD COLUMN artwork TYPE character varying(255);
|
||||
"""
|
||||
|
||||
DOWN = None
|
||||
|
|
|
@ -4,18 +4,12 @@ from django.db import migrations
|
|||
|
||||
from ._migrations import legacy_migration_factory
|
||||
|
||||
# This migration is currently a placeholder for 3.0.0-alpha.9.1.
|
||||
# Please do not remove it. There are currently no actions, but it
|
||||
# needs to remain intact so it does not fail when called from the
|
||||
# migrations script. Any future migrations that may apply to
|
||||
# 3.0.0-alpha.9.1 will be added to this file.
|
||||
|
||||
UP = """
|
||||
|
||||
ALTER TABLE cc_files ADD COLUMN artwork VARCHAR(4096);
|
||||
"""
|
||||
|
||||
DOWN = """
|
||||
|
||||
ALTER TABLE cc_files DROP COLUMN IF EXISTS artwork;
|
||||
"""
|
||||
|
||||
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
# pylint: disable=invalid-name
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
from ._migrations import legacy_migration_factory
|
||||
|
||||
UP = """
|
||||
ALTER TABLE cc_show ADD COLUMN override_intro_playlist boolean default 'f' NOT NULL;
|
||||
ALTER TABLE cc_show ADD COLUMN intro_playlist_id integer DEFAULT NULL;
|
||||
ALTER TABLE cc_show ADD CONSTRAINT cc_playlist_intro_playlist_fkey FOREIGN KEY (intro_playlist_id) REFERENCES cc_playlist (id) ON DELETE SET NULL;
|
||||
ALTER TABLE cc_show ADD COLUMN override_outro_playlist boolean default 'f' NOT NULL;
|
||||
ALTER TABLE cc_show ADD COLUMN outro_playlist_id integer DEFAULT NULL;
|
||||
ALTER TABLE cc_show ADD CONSTRAINT cc_playlist_outro_playlist_fkey FOREIGN KEY (outro_playlist_id) REFERENCES cc_playlist (id) ON DELETE SET NULL;
|
||||
"""
|
||||
|
||||
DOWN = """
|
||||
ALTER TABLE cc_show DROP COLUMN IF EXISTS override_intro_playlist;
|
||||
ALTER TABLE cc_show DROP COLUMN IF EXISTS intro_playlist_id;
|
||||
ALTER TABLE cc_show DROP CONSTRAINT IF EXISTS cc_playlist_intro_playlist_fkey;
|
||||
ALTER TABLE cc_show DROP COLUMN IF EXISTS override_outro_playlist;
|
||||
ALTER TABLE cc_show DROP COLUMN IF EXISTS outro_playlist_id;
|
||||
ALTER TABLE cc_show DROP CONSTRAINT IF EXISTS cc_playlist_outro_playlist_fkey;
|
||||
"""
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("legacy", "0045_add_sessions_table"),
|
||||
]
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=legacy_migration_factory(
|
||||
target="46",
|
||||
sql=UP,
|
||||
)
|
||||
)
|
||||
]
|
|
@ -1,2 +1,2 @@
|
|||
# The schema version is defined using the migration file prefix number
|
||||
LEGACY_SCHEMA_VERSION = "46"
|
||||
LEGACY_SCHEMA_VERSION = "45"
|
||||
|
|
|
@ -11,28 +11,15 @@ def get_schema_version():
|
|||
|
||||
Don't use django models as they might break in the future. Our concern is to upgrade
|
||||
the legacy database schema to the point where django is in charge of the migrations.
|
||||
|
||||
An airtime 2.5.1 migration will not have schema_version, in that case, we look for
|
||||
system_version to have a value of 2.5.1 and return that as the schema version value
|
||||
(really just needs to be anything besides None, so that the next migration doesn't overwrite
|
||||
the database)
|
||||
"""
|
||||
|
||||
if "cc_pref" not in connection.introspection.table_names():
|
||||
return None
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT valstr AS version
|
||||
FROM cc_pref
|
||||
WHERE (keystr = 'schema_version') OR (keystr = 'system_version' AND valstr = '2.5.1')
|
||||
"""
|
||||
)
|
||||
cursor.execute("SELECT valstr FROM cc_pref WHERE keystr = 'schema_version'")
|
||||
row = cursor.fetchone()
|
||||
if row and row[0]:
|
||||
return row[0]
|
||||
return None
|
||||
return row[0] if row else None
|
||||
|
||||
|
||||
def set_schema_version(cursor, version: str):
|
||||
|
|
|
@ -126,10 +126,6 @@ CREATE TABLE "cc_show"
|
|||
"has_autoplaylist" BOOLEAN DEFAULT 'f' NOT NULL,
|
||||
"autoplaylist_id" INTEGER,
|
||||
"autoplaylist_repeat" BOOLEAN DEFAULT 'f' NOT NULL,
|
||||
"override_intro_playlist" BOOLEAN DEFAULT 'f' NOT NULL,
|
||||
"intro_playlist_id" INTEGER,
|
||||
"override_outro_playlist" BOOLEAN DEFAULT 'f' NOT NULL,
|
||||
"outro_playlist_id" INTEGER,
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
|
@ -722,16 +718,6 @@ ALTER TABLE "cc_show" ADD CONSTRAINT "cc_playlist_autoplaylist_fkey"
|
|||
REFERENCES "cc_playlist" ("id")
|
||||
ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE "cc_show" ADD CONSTRAINT "cc_playlist_intro_playlist_fkey"
|
||||
FOREIGN KEY ("intro_playlist_id")
|
||||
REFERENCES "cc_playlist" ("id")
|
||||
ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE "cc_show" ADD CONSTRAINT "cc_playlist_outro_playlist_fkey"
|
||||
FOREIGN KEY ("outro_playlist_id")
|
||||
REFERENCES "cc_playlist" ("id")
|
||||
ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE "cc_show_instances" ADD CONSTRAINT "cc_show_fkey"
|
||||
FOREIGN KEY ("show_id")
|
||||
REFERENCES "cc_show" ("id")
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
from .readwriteserializer import ReadWriteSerializerMixin
|
|
@ -1,33 +0,0 @@
|
|||
from rest_framework.serializers import Serializer
|
||||
|
||||
|
||||
class ReadWriteSerializerMixin:
|
||||
"""
|
||||
Overrides get_serializer_class to choose the read serializer
|
||||
for GET requests and the write serializer for POST requests.
|
||||
|
||||
Set read_serializer_class and write_serializer_class attributes on a
|
||||
viewset.
|
||||
"""
|
||||
|
||||
read_serializer_class = Serializer
|
||||
write_serializer_class = Serializer
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ["create"]:
|
||||
return self.get_write_serializer_class()
|
||||
return self.get_read_serializer_class()
|
||||
|
||||
def get_read_serializer_class(self):
|
||||
assert self.read_serializer_class is not None, (
|
||||
f"'{self.__class__.__name__}' should either include a `read_serializer_class`"
|
||||
"attribute, or override the `get_read_serializer_class()` method."
|
||||
)
|
||||
return self.read_serializer_class
|
||||
|
||||
def get_write_serializer_class(self):
|
||||
assert self.write_serializer_class is not None, (
|
||||
f"'{self.__class__.__name__}' should either include a `write_serializer_class`"
|
||||
"attribute, or override the `get_write_serializer_class()` method."
|
||||
)
|
||||
return self.write_serializer_class
|
|
@ -1,4 +1,4 @@
|
|||
from .core.models.role import Role
|
||||
from .core.models import Role
|
||||
|
||||
GUEST_PERMISSIONS = [
|
||||
"view_schedule",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
|
||||
|
||||
class Schedule(models.Model):
|
||||
|
@ -116,14 +115,6 @@ class Schedule(models.Model):
|
|||
return self.instance.ends_at
|
||||
return self.ends_at
|
||||
|
||||
@staticmethod
|
||||
def is_file_scheduled_in_the_future(file_id):
|
||||
count = Schedule.objects.filter(
|
||||
file_id=file_id,
|
||||
ends_at__gt=now(),
|
||||
).count()
|
||||
return count > 0
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = "cc_schedule"
|
||||
|
|
|
@ -69,29 +69,7 @@ class Show(models.Model):
|
|||
auto_playlist_enabled = models.BooleanField(db_column="has_autoplaylist")
|
||||
auto_playlist_repeat = models.BooleanField(db_column="autoplaylist_repeat")
|
||||
|
||||
intro_playlist = models.ForeignKey(
|
||||
"schedule.Playlist",
|
||||
on_delete=models.DO_NOTHING,
|
||||
blank=True,
|
||||
null=True,
|
||||
db_column="intro_playlist_id",
|
||||
related_name="intro_playlist",
|
||||
)
|
||||
|
||||
override_intro_playlist = models.BooleanField(db_column="override_intro_playlist")
|
||||
|
||||
outro_playlist = models.ForeignKey(
|
||||
"schedule.Playlist",
|
||||
on_delete=models.DO_NOTHING,
|
||||
blank=True,
|
||||
null=True,
|
||||
db_column="outro_playlist_id",
|
||||
related_name="outro_playlist",
|
||||
)
|
||||
|
||||
override_outro_playlist = models.BooleanField(db_column="override_outro_playlist")
|
||||
|
||||
hosts = models.ManyToManyField( # type: ignore[var-annotated]
|
||||
hosts = models.ManyToManyField(
|
||||
"core.User",
|
||||
through="ShowHost",
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from .playlist import PlaylistContentSerializer, PlaylistSerializer
|
||||
from .schedule import ReadScheduleSerializer, WriteScheduleSerializer
|
||||
from .schedule import ScheduleSerializer
|
||||
from .show import (
|
||||
ShowDaysSerializer,
|
||||
ShowHostSerializer,
|
||||
|
|
|
@ -3,17 +3,10 @@ from rest_framework import serializers
|
|||
from ..models import Schedule
|
||||
|
||||
|
||||
class ReadScheduleSerializer(serializers.ModelSerializer):
|
||||
class ScheduleSerializer(serializers.ModelSerializer):
|
||||
cue_out = serializers.DurationField(source="get_cue_out", read_only=True)
|
||||
ends_at = serializers.DateTimeField(source="get_ends_at", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Schedule
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class WriteScheduleSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Schedule
|
||||
fields = "__all__"
|
||||
|
|
|
@ -21,10 +21,6 @@ class ShowSerializer(serializers.ModelSerializer):
|
|||
"auto_playlist",
|
||||
"auto_playlist_enabled",
|
||||
"auto_playlist_repeat",
|
||||
"intro_playlist",
|
||||
"override_intro_playlist",
|
||||
"outro_playlist",
|
||||
"override_outro_playlist",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -2,9 +2,8 @@ from django.db import models
|
|||
from django_filters import rest_framework as filters
|
||||
from rest_framework import viewsets
|
||||
|
||||
from ...mixins import ReadWriteSerializerMixin
|
||||
from ..models import Schedule
|
||||
from ..serializers import ReadScheduleSerializer, WriteScheduleSerializer
|
||||
from ..serializers import ScheduleSerializer
|
||||
|
||||
|
||||
class ScheduleFilter(filters.FilterSet):
|
||||
|
@ -27,9 +26,8 @@ class ScheduleFilter(filters.FilterSet):
|
|||
fields = [] # type: ignore
|
||||
|
||||
|
||||
class ScheduleViewSet(ReadWriteSerializerMixin, viewsets.ModelViewSet):
|
||||
class ScheduleViewSet(viewsets.ModelViewSet):
|
||||
queryset = Schedule.objects.all()
|
||||
read_serializer_class = ReadScheduleSerializer
|
||||
write_serializer_class = WriteScheduleSerializer
|
||||
serializer_class = ScheduleSerializer
|
||||
filterset_class = ScheduleFilter
|
||||
model_permission_name = "schedule"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from os import getenv
|
||||
from warnings import warn
|
||||
|
||||
# pylint: disable=unused-import
|
||||
from ._internal import (
|
||||
|
@ -24,7 +25,15 @@ LIBRETIME_CONFIG_FILEPATH = getenv("LIBRETIME_CONFIG_FILEPATH")
|
|||
|
||||
CONFIG = Config(LIBRETIME_CONFIG_FILEPATH) # type: ignore[arg-type, misc]
|
||||
|
||||
SECRET_KEY = CONFIG.general.secret_key
|
||||
if CONFIG.general.secret_key is None:
|
||||
warn(
|
||||
"The [general.secret_key] configuration field is not set but will be required "
|
||||
"in the next major release. Using [general.api_key] as fallback.",
|
||||
FutureWarning,
|
||||
)
|
||||
SECRET_KEY = CONFIG.general.api_key
|
||||
else:
|
||||
SECRET_KEY = CONFIG.general.secret_key
|
||||
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
|
|
|
@ -1,96 +1,35 @@
|
|||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.conf import settings
|
||||
from model_bakery import baker
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from ...._fixtures import AUDIO_FILENAME
|
||||
from ...models import File
|
||||
|
||||
|
||||
class TestFileViewSet(APITestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.path = "/api/v2/files/{id}/download"
|
||||
cls.token = settings.CONFIG.general.api_key
|
||||
|
||||
def test_download_invalid(self):
|
||||
def test_invalid(self):
|
||||
path = self.path.format(id="a")
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {self.token}")
|
||||
file_id = "1"
|
||||
response = self.client.get(f"/api/v2/files/{file_id}/download")
|
||||
response = self.client.get(path)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_does_not_exist(self):
|
||||
path = self.path.format(id="1")
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {self.token}")
|
||||
response = self.client.get(path)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_download(self):
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {self.token}")
|
||||
file: File = baker.make(
|
||||
def test_exists(self):
|
||||
file = baker.make(
|
||||
"storage.File",
|
||||
mime="audio/mp3",
|
||||
filepath=AUDIO_FILENAME,
|
||||
)
|
||||
response = self.client.get(f"/api/v2/files/{file.id}/download")
|
||||
path = self.path.format(id=str(file.pk))
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {self.token}")
|
||||
response = self.client.get(path)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_destroy(self):
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {self.token}")
|
||||
file: File = baker.make(
|
||||
"storage.File",
|
||||
mime="audio/mp3",
|
||||
filepath=AUDIO_FILENAME,
|
||||
)
|
||||
|
||||
with patch("libretime_api.storage.views.file.remove") as remove_mock:
|
||||
response = self.client.delete(f"/api/v2/files/{file.id}")
|
||||
|
||||
self.assertEqual(response.status_code, 204)
|
||||
remove_mock.assert_called_with(
|
||||
os.path.join(settings.CONFIG.storage.path, file.filepath)
|
||||
)
|
||||
|
||||
def test_destroy_no_file(self):
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {self.token}")
|
||||
file = baker.make(
|
||||
"storage.File",
|
||||
mime="audio/mp3",
|
||||
filepath="invalid.mp3",
|
||||
)
|
||||
response = self.client.delete(f"/api/v2/files/{file.id}")
|
||||
self.assertEqual(response.status_code, 204)
|
||||
|
||||
def test_destroy_invalid(self):
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {self.token}")
|
||||
file_id = "1"
|
||||
response = self.client.delete(f"/api/v2/files/{file_id}")
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_filters(self):
|
||||
file = baker.make(
|
||||
"storage.File",
|
||||
mime="audio/mp3",
|
||||
filepath=AUDIO_FILENAME,
|
||||
genre="Soul",
|
||||
md5="5a11ffe0e6c6d70fcdbad1b734be6482",
|
||||
)
|
||||
baker.make(
|
||||
"storage.File",
|
||||
mime="audio/mp3",
|
||||
filepath=AUDIO_FILENAME,
|
||||
genre="R&B",
|
||||
md5="5a11ffe0e6c6d70fcdbad1b734be6483",
|
||||
)
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {self.token}")
|
||||
|
||||
path = "/api/v2/files"
|
||||
results = self.client.get(path).json()
|
||||
self.assertEqual(len(results), 2)
|
||||
|
||||
path = f"/api/v2/files?md5={file.md5}"
|
||||
results = self.client.get(path).json()
|
||||
self.assertEqual(len(results), 1)
|
||||
|
||||
path = "/api/v2/files?genre=Soul"
|
||||
results = self.client.get(path).json()
|
||||
self.assertEqual(len(results), 1)
|
||||
|
||||
path = "/api/v2/files?genre=R%26B"
|
||||
results = self.client.get(path).json()
|
||||
self.assertEqual(len(results), 1)
|
||||
|
|
|
@ -1,62 +1,25 @@
|
|||
import logging
|
||||
import os
|
||||
from os import remove
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
from django.utils.encoding import filepath_to_uri
|
||||
from django_filters import rest_framework as filters
|
||||
from rest_framework import status, viewsets
|
||||
from django.http import FileResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.serializers import IntegerField
|
||||
|
||||
from ...schedule.models import Schedule
|
||||
from ..models import File
|
||||
from ..serializers import FileSerializer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FileInUse(APIException):
|
||||
status_code = status.HTTP_409_CONFLICT
|
||||
default_detail = "The file is currently used"
|
||||
default_code = "file_in_use"
|
||||
|
||||
|
||||
class FileViewSet(viewsets.ModelViewSet):
|
||||
queryset = File.objects.all()
|
||||
serializer_class = FileSerializer
|
||||
model_permission_name = "file"
|
||||
filter_backends = (filters.DjangoFilterBackend,)
|
||||
filterset_fields = ("md5", "genre")
|
||||
|
||||
# pylint: disable=invalid-name,unused-argument
|
||||
@action(detail=True, methods=["GET"])
|
||||
def download(self, request, pk=None):
|
||||
instance: File = self.get_object()
|
||||
def download(self, request, pk=None): # pylint: disable=invalid-name
|
||||
pk = IntegerField().to_internal_value(data=pk)
|
||||
|
||||
response = HttpResponse()
|
||||
# HTTP headers must be USASCII encoded, or Nginx might not find the file and
|
||||
# will return a 404.
|
||||
redirect_uri = filepath_to_uri(os.path.join("/api/_media", instance.filepath))
|
||||
response["X-Accel-Redirect"] = redirect_uri
|
||||
return response
|
||||
|
||||
def perform_destroy(self, instance: File):
|
||||
if Schedule.is_file_scheduled_in_the_future(file_id=instance.id):
|
||||
raise FileInUse("file is scheduled in the future")
|
||||
|
||||
try:
|
||||
if instance.filepath is None:
|
||||
logger.warning("file does not have a filepath: %d", instance.id)
|
||||
return
|
||||
|
||||
path = os.path.join(settings.CONFIG.storage.path, instance.filepath)
|
||||
|
||||
if not os.path.isfile(path):
|
||||
logger.warning("file does not exist in storage: %d", instance.id)
|
||||
return
|
||||
|
||||
remove(path)
|
||||
except OSError as exception:
|
||||
raise APIException("could not delete file from storage") from exception
|
||||
file = get_object_or_404(File, pk=pk)
|
||||
path = os.path.join(settings.CONFIG.storage.path, file.filepath)
|
||||
return FileResponse(open(path, "rb"), content_type=file.mime)
|
||||
|
|
|
@ -4,7 +4,6 @@ URL Configuration
|
|||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/topics/http/urls/
|
||||
"""
|
||||
|
||||
from django.urls import include, path
|
||||
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ django-settings-module = "libretime_api.settings.testing"
|
|||
[tool.pylint.messages_control]
|
||||
extension-pkg-whitelist = "pydantic"
|
||||
disable = [
|
||||
"duplicate-code",
|
||||
"fixme",
|
||||
"missing-class-docstring",
|
||||
"missing-function-docstring",
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# Please do not edit this file, edit the setup.py file!
|
||||
# This file is auto-generated by tools/extract_requirements.py.
|
||||
django-cors-headers>=3.14.0,<4.5
|
||||
django-filter>=2.4.0,<24.4
|
||||
django-cors-headers>=3.14.0,<4.4
|
||||
django-filter>=2.4.0,<23.4
|
||||
django>=4.2.0,<4.3
|
||||
djangorestframework>=3.14.0,<3.16
|
||||
drf-spectacular>=0.22.1,<0.29
|
||||
gunicorn>=22.0.0,<23.1
|
||||
djangorestframework>=3.14.0,<3.15
|
||||
drf-spectacular>=0.22.1,<0.27
|
||||
gunicorn>=20.1.0,<21.3
|
||||
psycopg[c]>=3.1.8,<3.2
|
||||
requests>=2.32.2,<2.33
|
||||
uvicorn[standard]>=0.17.6,<0.33.0
|
||||
requests>=2.31.0,<2.32
|
||||
uvicorn[standard]>=0.17.6,<0.24.0
|
||||
|
|
256
api/schema.yml
256
api/schema.yml
|
@ -154,15 +154,6 @@ paths:
|
|||
/api/v2/files:
|
||||
get:
|
||||
operationId: files_list
|
||||
parameters:
|
||||
- in: query
|
||||
name: genre
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: md5
|
||||
schema:
|
||||
type: string
|
||||
tags:
|
||||
- files
|
||||
security:
|
||||
|
@ -2561,12 +2552,6 @@ paths:
|
|||
/api/v2/schedule:
|
||||
get:
|
||||
operationId: schedule_list
|
||||
description: |-
|
||||
Overrides get_serializer_class to choose the read serializer
|
||||
for GET requests and the write serializer for POST requests.
|
||||
|
||||
Set read_serializer_class and write_serializer_class attributes on a
|
||||
viewset.
|
||||
parameters:
|
||||
- in: query
|
||||
name: broadcasted
|
||||
|
@ -2612,29 +2597,23 @@ paths:
|
|||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ReadSchedule"
|
||||
$ref: "#/components/schemas/Schedule"
|
||||
description: ""
|
||||
post:
|
||||
operationId: schedule_create
|
||||
description: |-
|
||||
Overrides get_serializer_class to choose the read serializer
|
||||
for GET requests and the write serializer for POST requests.
|
||||
|
||||
Set read_serializer_class and write_serializer_class attributes on a
|
||||
viewset.
|
||||
tags:
|
||||
- schedule
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/WriteSchedule"
|
||||
$ref: "#/components/schemas/Schedule"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: "#/components/schemas/WriteSchedule"
|
||||
$ref: "#/components/schemas/Schedule"
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: "#/components/schemas/WriteSchedule"
|
||||
$ref: "#/components/schemas/Schedule"
|
||||
required: true
|
||||
security:
|
||||
- cookieAuth: []
|
||||
|
@ -2644,17 +2623,11 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/WriteSchedule"
|
||||
$ref: "#/components/schemas/Schedule"
|
||||
description: ""
|
||||
/api/v2/schedule/{id}:
|
||||
get:
|
||||
operationId: schedule_retrieve
|
||||
description: |-
|
||||
Overrides get_serializer_class to choose the read serializer
|
||||
for GET requests and the write serializer for POST requests.
|
||||
|
||||
Set read_serializer_class and write_serializer_class attributes on a
|
||||
viewset.
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
|
@ -2672,16 +2645,10 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ReadSchedule"
|
||||
$ref: "#/components/schemas/Schedule"
|
||||
description: ""
|
||||
put:
|
||||
operationId: schedule_update
|
||||
description: |-
|
||||
Overrides get_serializer_class to choose the read serializer
|
||||
for GET requests and the write serializer for POST requests.
|
||||
|
||||
Set read_serializer_class and write_serializer_class attributes on a
|
||||
viewset.
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
|
@ -2695,13 +2662,13 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ReadSchedule"
|
||||
$ref: "#/components/schemas/Schedule"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ReadSchedule"
|
||||
$ref: "#/components/schemas/Schedule"
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ReadSchedule"
|
||||
$ref: "#/components/schemas/Schedule"
|
||||
required: true
|
||||
security:
|
||||
- cookieAuth: []
|
||||
|
@ -2711,16 +2678,10 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ReadSchedule"
|
||||
$ref: "#/components/schemas/Schedule"
|
||||
description: ""
|
||||
patch:
|
||||
operationId: schedule_partial_update
|
||||
description: |-
|
||||
Overrides get_serializer_class to choose the read serializer
|
||||
for GET requests and the write serializer for POST requests.
|
||||
|
||||
Set read_serializer_class and write_serializer_class attributes on a
|
||||
viewset.
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
|
@ -2734,13 +2695,13 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PatchedReadSchedule"
|
||||
$ref: "#/components/schemas/PatchedSchedule"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PatchedReadSchedule"
|
||||
$ref: "#/components/schemas/PatchedSchedule"
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: "#/components/schemas/PatchedReadSchedule"
|
||||
$ref: "#/components/schemas/PatchedSchedule"
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- basicAuth: []
|
||||
|
@ -2749,16 +2710,10 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ReadSchedule"
|
||||
$ref: "#/components/schemas/Schedule"
|
||||
description: ""
|
||||
delete:
|
||||
operationId: schedule_destroy
|
||||
description: |-
|
||||
Overrides get_serializer_class to choose the read serializer
|
||||
for GET requests and the write serializer for POST requests.
|
||||
|
||||
Set read_serializer_class and write_serializer_class attributes on a
|
||||
viewset.
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
|
@ -5357,7 +5312,7 @@ components:
|
|||
readOnly: true
|
||||
import_status:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/FileImportStatusEnum"
|
||||
- $ref: "#/components/schemas/PlaylistContentKindEnum"
|
||||
minimum: -2147483648
|
||||
maximum: 2147483647
|
||||
filepath:
|
||||
|
@ -5609,16 +5564,6 @@ components:
|
|||
- mime
|
||||
- name
|
||||
- size
|
||||
FileImportStatusEnum:
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
- 2
|
||||
type: integer
|
||||
description: |-
|
||||
* `0` - Success
|
||||
* `1` - Pending
|
||||
* `2` - Failed
|
||||
ImportedPodcast:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -5769,7 +5714,7 @@ components:
|
|||
readOnly: true
|
||||
import_status:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/FileImportStatusEnum"
|
||||
- $ref: "#/components/schemas/PlaylistContentKindEnum"
|
||||
minimum: -2147483648
|
||||
maximum: 2147483647
|
||||
filepath:
|
||||
|
@ -6345,7 +6290,7 @@ components:
|
|||
user:
|
||||
type: integer
|
||||
nullable: true
|
||||
PatchedReadSchedule:
|
||||
PatchedSchedule:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
|
@ -6454,16 +6399,6 @@ components:
|
|||
type: boolean
|
||||
auto_playlist_repeat:
|
||||
type: boolean
|
||||
intro_playlist:
|
||||
type: integer
|
||||
nullable: true
|
||||
override_intro_playlist:
|
||||
type: boolean
|
||||
outro_playlist:
|
||||
type: integer
|
||||
nullable: true
|
||||
override_outro_playlist:
|
||||
type: boolean
|
||||
PatchedShowDays:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -6922,9 +6857,9 @@ components:
|
|||
- 2
|
||||
type: integer
|
||||
description: |-
|
||||
* `0` - File
|
||||
* `1` - Stream
|
||||
* `2` - Block
|
||||
* `0` - Success
|
||||
* `1` - Pending
|
||||
* `2` - Failed
|
||||
PlayoutHistory:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -7141,7 +7076,41 @@ components:
|
|||
- id
|
||||
- key
|
||||
- user
|
||||
ReadSchedule:
|
||||
RecordEnabledEnum:
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
type: integer
|
||||
description: |-
|
||||
* `0` - No
|
||||
* `1` - Yes
|
||||
RepeatKindEnum:
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
- 4
|
||||
- 5
|
||||
- 2
|
||||
type: integer
|
||||
description: |-
|
||||
* `0` - Every week
|
||||
* `1` - Every 2 weeks
|
||||
* `4` - Every 3 weeks
|
||||
* `5` - Every 4 weeks
|
||||
* `2` - Every month
|
||||
RoleEnum:
|
||||
enum:
|
||||
- G
|
||||
- H
|
||||
- P
|
||||
- A
|
||||
type: string
|
||||
description: |-
|
||||
* `G` - Guest
|
||||
* `H` - Host
|
||||
* `P` - Manager
|
||||
* `A` - Admin
|
||||
Schedule:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
|
@ -7203,40 +7172,6 @@ components:
|
|||
- instance
|
||||
- position
|
||||
- starts_at
|
||||
RecordEnabledEnum:
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
type: integer
|
||||
description: |-
|
||||
* `0` - No
|
||||
* `1` - Yes
|
||||
RepeatKindEnum:
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
- 4
|
||||
- 5
|
||||
- 2
|
||||
type: integer
|
||||
description: |-
|
||||
* `0` - Every week
|
||||
* `1` - Every 2 weeks
|
||||
* `4` - Every 3 weeks
|
||||
* `5` - Every 4 weeks
|
||||
* `2` - Every month
|
||||
RoleEnum:
|
||||
enum:
|
||||
- G
|
||||
- H
|
||||
- P
|
||||
- A
|
||||
type: string
|
||||
description: |-
|
||||
* `G` - Guest
|
||||
* `H` - Host
|
||||
* `P` - Manager
|
||||
* `A` - Admin
|
||||
ServiceRegister:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -7296,16 +7231,6 @@ components:
|
|||
type: boolean
|
||||
auto_playlist_repeat:
|
||||
type: boolean
|
||||
intro_playlist:
|
||||
type: integer
|
||||
nullable: true
|
||||
override_intro_playlist:
|
||||
type: boolean
|
||||
outro_playlist:
|
||||
type: integer
|
||||
nullable: true
|
||||
override_outro_playlist:
|
||||
type: boolean
|
||||
required:
|
||||
- auto_playlist_enabled
|
||||
- auto_playlist_repeat
|
||||
|
@ -7314,8 +7239,6 @@ components:
|
|||
- linked
|
||||
- live_enabled
|
||||
- name
|
||||
- override_intro_playlist
|
||||
- override_outro_playlist
|
||||
ShowDays:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -7602,19 +7525,10 @@ components:
|
|||
message_offline:
|
||||
type: string
|
||||
readOnly: true
|
||||
replay_gain_enabled:
|
||||
type: boolean
|
||||
readOnly: true
|
||||
replay_gain_offset:
|
||||
type: number
|
||||
format: double
|
||||
readOnly: true
|
||||
required:
|
||||
- input_fade_transition
|
||||
- message_format
|
||||
- message_offline
|
||||
- replay_gain_enabled
|
||||
- replay_gain_offset
|
||||
StreamState:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -7840,66 +7754,6 @@ components:
|
|||
* `4` - Friday
|
||||
* `5` - Saturday
|
||||
* `6` - Sunday
|
||||
WriteSchedule:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
readOnly: true
|
||||
starts_at:
|
||||
type: string
|
||||
format: date-time
|
||||
ends_at:
|
||||
type: string
|
||||
format: date-time
|
||||
length:
|
||||
type: string
|
||||
nullable: true
|
||||
fade_in:
|
||||
type: string
|
||||
format: time
|
||||
nullable: true
|
||||
fade_out:
|
||||
type: string
|
||||
format: time
|
||||
nullable: true
|
||||
cue_in:
|
||||
type: string
|
||||
cue_out:
|
||||
type: string
|
||||
position:
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: -2147483648
|
||||
position_status:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/PositionStatusEnum"
|
||||
minimum: -32768
|
||||
maximum: 32767
|
||||
broadcasted:
|
||||
type: integer
|
||||
maximum: 32767
|
||||
minimum: -32768
|
||||
played:
|
||||
type: boolean
|
||||
nullable: true
|
||||
instance:
|
||||
type: integer
|
||||
file:
|
||||
type: integer
|
||||
nullable: true
|
||||
stream:
|
||||
type: integer
|
||||
nullable: true
|
||||
required:
|
||||
- broadcasted
|
||||
- cue_in
|
||||
- cue_out
|
||||
- ends_at
|
||||
- id
|
||||
- instance
|
||||
- position
|
||||
- starts_at
|
||||
securitySchemes:
|
||||
basicAuth:
|
||||
type: http
|
||||
|
|
20
api/setup.py
20
api/setup.py
|
@ -1,10 +1,8 @@
|
|||
from setuptools import find_packages, setup
|
||||
|
||||
version = "4.2.0" # x-release-please-version
|
||||
|
||||
setup(
|
||||
name="libretime-api",
|
||||
version=version,
|
||||
version="3.2.0",
|
||||
description="LibreTime API",
|
||||
author="LibreTime Contributors",
|
||||
url="https://github.com/libretime/libretime",
|
||||
|
@ -26,22 +24,22 @@ setup(
|
|||
]
|
||||
},
|
||||
install_requires=[
|
||||
"django-cors-headers>=3.14.0,<4.5",
|
||||
"django-filter>=2.4.0,<24.4",
|
||||
"django-cors-headers>=3.14.0,<4.4",
|
||||
"django-filter>=2.4.0,<23.4",
|
||||
"django>=4.2.0,<4.3",
|
||||
"djangorestframework>=3.14.0,<3.16",
|
||||
"drf-spectacular>=0.22.1,<0.29",
|
||||
"requests>=2.32.2,<2.33",
|
||||
"djangorestframework>=3.14.0,<3.15",
|
||||
"drf-spectacular>=0.22.1,<0.27",
|
||||
"requests>=2.31.0,<2.32",
|
||||
],
|
||||
extras_require={
|
||||
"prod": [
|
||||
"gunicorn>=22.0.0,<23.1",
|
||||
"gunicorn>=20.1.0,<21.3",
|
||||
"psycopg[c]>=3.1.8,<3.2",
|
||||
"uvicorn[standard]>=0.17.6,<0.33.0",
|
||||
"uvicorn[standard]>=0.17.6,<0.24.0",
|
||||
],
|
||||
"dev": [
|
||||
"django-coverage-plugin>=3.0.0,<4",
|
||||
"django-stubs>=5.1.0,<6",
|
||||
"django-stubs>=1.14.0,<5",
|
||||
"djangorestframework-stubs>=1.8.0,<4",
|
||||
"model_bakery>=1.10.1,<2",
|
||||
"psycopg[binary]>=3.1.8,<4",
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
#cloud-config
|
||||
package_update: true
|
||||
package_upgrade: true
|
||||
# Maintainer: Zachary Klosko (kloskoz@vcu.edu)
|
||||
|
||||
hostname: libretimeTest
|
||||
timezone: America/New York # change as needed
|
||||
ntp:
|
||||
pools: ["north-america.pool.ntp.org"]
|
||||
servers: ["0.north-america.pool.ntp.org", "0.pool.ntp.org"]
|
||||
|
||||
password: hackme
|
||||
chpasswd: { expire: False }
|
||||
|
||||
packages:
|
||||
- git
|
||||
apt_update: true
|
||||
apt_upgrade: true
|
||||
|
||||
# Clone repo on init (Change repo url if needed)
|
||||
# If you need to clone a branch, use git clone --branch branchname repourl
|
||||
runcmd:
|
||||
- git clone https://github.com/libretime/libretime /root/libretime
|
||||
- cd /root/libretime
|
||||
- HOME=/root bash install "http://$(ip route get 8.8.8.8 | awk '{print $7}'):8080"
|
||||
- git clone https://github.com/libretime/libretime
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
# This file is used for development. It it not intended for production!
|
||||
# See https://libretime.org/docs/developer-manual/development/environment/#docker-compose
|
||||
#
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
ports:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
version: "3.9"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
|
@ -10,13 +12,13 @@ services:
|
|||
test: pg_isready -U libretime
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.13-alpine
|
||||
image: rabbitmq:3.12-alpine
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_DEFAULT_VHOST:-/libretime}
|
||||
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-libretime}
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS:-libretime} # Change me !
|
||||
healthcheck:
|
||||
test: nc -z 127.0.0.1 5672
|
||||
test: rabbitmq-diagnostics -q ping
|
||||
|
||||
playout:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-latest}
|
||||
|
@ -29,7 +31,7 @@ services:
|
|||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_playout:/app
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx:8080
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx
|
||||
|
||||
liquidsoap:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-latest}
|
||||
|
@ -46,7 +48,7 @@ services:
|
|||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_playout:/app
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx:8080
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx
|
||||
|
||||
analyzer:
|
||||
image: ghcr.io/libretime/libretime-analyzer:${LIBRETIME_VERSION:-latest}
|
||||
|
@ -59,7 +61,7 @@ services:
|
|||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx:8080
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx
|
||||
|
||||
worker:
|
||||
image: ghcr.io/libretime/libretime-worker:${LIBRETIME_VERSION:-latest}
|
||||
|
@ -71,7 +73,7 @@ services:
|
|||
volumes:
|
||||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.yml}:/etc/libretime/config.yml:ro
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx:8080
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx
|
||||
|
||||
api:
|
||||
image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-latest}
|
||||
|
@ -101,12 +103,11 @@ services:
|
|||
nginx:
|
||||
image: nginx
|
||||
ports:
|
||||
- 8080:8080
|
||||
- 8080:80
|
||||
depends_on:
|
||||
- legacy
|
||||
volumes:
|
||||
- libretime_assets:/var/www/html:ro
|
||||
- libretime_storage:/srv/libretime:ro
|
||||
- ${NGINX_CONFIG_FILEPATH:-./nginx.conf}:/etc/nginx/conf.d/default.conf:ro
|
||||
|
||||
icecast:
|
||||
|
|
|
@ -7,8 +7,9 @@ general:
|
|||
# The internal API authentication key.
|
||||
# > this field is REQUIRED
|
||||
api_key:
|
||||
# The Django API secret key.
|
||||
# > this field is REQUIRED
|
||||
# The Django API secret key. If not defined, the value of [general.api_key] will be
|
||||
# used as fallback.
|
||||
# > this field will be REQUIRED starting with LibreTime 4.0.0
|
||||
secret_key:
|
||||
|
||||
# List of origins allowed to access resources on the server, the public url
|
||||
|
@ -31,8 +32,7 @@ general:
|
|||
auth: local
|
||||
|
||||
storage:
|
||||
# Path of the storage directory. Make sure to update the Nginx configuration after
|
||||
# updating the storage path.
|
||||
# Path of the storage directory.
|
||||
# > default is /srv/libretime
|
||||
path: /srv/libretime
|
||||
|
||||
|
@ -325,9 +325,5 @@ stream:
|
|||
enabled: false
|
||||
# System output kind.
|
||||
# > must be one of (alsa, ao, oss, portaudio, pulseaudio)
|
||||
# > default is pulseaudio
|
||||
kind: pulseaudio
|
||||
|
||||
# System output device.
|
||||
# > only available for kind=(alsa, pulseaudio)
|
||||
device:
|
||||
# > default is alsa
|
||||
kind: alsa
|
||||
|
|
|
@ -7,8 +7,9 @@ general:
|
|||
# The internal API authentication key.
|
||||
# > this field is REQUIRED
|
||||
api_key:
|
||||
# The Django API secret key.
|
||||
# > this field is REQUIRED
|
||||
# The Django API secret key. If not defined, the value of [general.api_key] will be
|
||||
# used as fallback.
|
||||
# > this field will be REQUIRED starting with LibreTime 4.0.0
|
||||
secret_key:
|
||||
|
||||
# List of origins allowed to access resources on the server, the public url
|
||||
|
@ -31,8 +32,7 @@ general:
|
|||
auth: local
|
||||
|
||||
storage:
|
||||
# Path of the storage directory. Make sure to update the Nginx configuration after
|
||||
# updating the storage path.
|
||||
# Path of the storage directory.
|
||||
# > default is /srv/libretime
|
||||
path: /srv/libretime
|
||||
|
||||
|
@ -325,9 +325,5 @@ stream:
|
|||
enabled: false
|
||||
# System output kind.
|
||||
# > must be one of (alsa, ao, oss, portaudio, pulseaudio)
|
||||
# > default is pulseaudio
|
||||
kind: pulseaudio
|
||||
|
||||
# System output device.
|
||||
# > only available for kind=(alsa, pulseaudio)
|
||||
device:
|
||||
# > default is alsa
|
||||
kind: alsa
|
||||
|
|
|
@ -7,8 +7,9 @@ general:
|
|||
# The internal API authentication key.
|
||||
# > this field is REQUIRED
|
||||
api_key: some_secret_api_key
|
||||
# The Django API secret key.
|
||||
# > this field is REQUIRED
|
||||
# The Django API secret key. If not defined, the value of [general.api_key] will be
|
||||
# used as fallback.
|
||||
# > this field will be REQUIRED starting with LibreTime 4.0.0
|
||||
secret_key:
|
||||
|
||||
# List of origins allowed to access resources on the server, the public url
|
||||
|
@ -31,8 +32,7 @@ general:
|
|||
auth: local
|
||||
|
||||
storage:
|
||||
# Path of the storage directory. Make sure to update the Nginx configuration after
|
||||
# updating the storage path.
|
||||
# Path of the storage directory.
|
||||
# > default is /srv/libretime
|
||||
path: /srv/libretime
|
||||
|
||||
|
@ -325,9 +325,5 @@ stream:
|
|||
enabled: false
|
||||
# System output kind.
|
||||
# > must be one of (alsa, ao, oss, portaudio, pulseaudio)
|
||||
# > default is pulseaudio
|
||||
kind: pulseaudio
|
||||
|
||||
# System output device.
|
||||
# > only available for kind=(alsa, pulseaudio)
|
||||
device:
|
||||
# > default is alsa
|
||||
kind: alsa
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
server {
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
root /var/www/html/public;
|
||||
|
||||
|
@ -40,11 +40,4 @@ server {
|
|||
proxy_redirect off;
|
||||
proxy_pass http://api:9001;
|
||||
}
|
||||
|
||||
# Internal path for serving media files from the API.
|
||||
location /api/_media {
|
||||
internal;
|
||||
# This alias path must match the 'storage.path' configuration field.
|
||||
alias /srv/libretime;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,8 +42,8 @@ general:
|
|||
# The internal API authentication key.
|
||||
# > this field is REQUIRED
|
||||
api_key: "some_random_generated_secret!"
|
||||
# The Django API secret key.
|
||||
# > this field is REQUIRED
|
||||
# The Django API secret key. If not defined, the value of [general.api_key] will be
|
||||
# used as fallback.
|
||||
secret_key: "some_random_generated_secret!"
|
||||
|
||||
# List of origins allowed to access resources on the server,
|
||||
|
@ -72,32 +72,11 @@ The `storage` section configure the project storage.
|
|||
|
||||
```yml
|
||||
storage:
|
||||
# Path of the storage directory. Make sure to update the Nginx configuration after
|
||||
# updating the storage path.
|
||||
# Path of the storage directory.
|
||||
# > default is /srv/libretime
|
||||
path: "/srv/libretime"
|
||||
```
|
||||
|
||||
:::caution
|
||||
|
||||
After editing the `storage.path` field, make sure to update the LibreTime Nginx configuration file with the new value.
|
||||
|
||||
In the example below, we are changing the path from `/srv/libretime` to `/mnt/data`:
|
||||
|
||||
```patch
|
||||
...
|
||||
|
||||
# Internal path for serving media files from the API.
|
||||
location /api/_media {
|
||||
internal;
|
||||
# This alias path must match the 'storage.path' configuration field.
|
||||
- alias /srv/libretime;
|
||||
+ alias /mnt/data;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Database
|
||||
|
||||
The `database` section configure the PostgreSQL connection.
|
||||
|
@ -547,15 +526,11 @@ stream:
|
|||
system:
|
||||
- # Whether the output is enabled.
|
||||
# > default is false
|
||||
enabled: true
|
||||
enabled: false
|
||||
# System output kind.
|
||||
# > must be one of (alsa, ao, oss, portaudio, pulseaudio)
|
||||
# > default is pulseaudio
|
||||
kind: "pulseaudio"
|
||||
|
||||
# System output device.
|
||||
# > only available for kind=(alsa, pulseaudio)
|
||||
device: "alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp__sink"
|
||||
# > default is alsa
|
||||
kind: "alsa"
|
||||
```
|
||||
|
||||
## LDAP
|
||||
|
|
|
@ -50,7 +50,7 @@ check them against pam.
|
|||
|
||||
The above configuration expects a PAM configuration for the `http-libretime` service.
|
||||
|
||||
To configure this you need to create the file `/etc/pam.d/http-libretime` with the following contents.
|
||||
To confiure this you need to create the file `/etc/pam.d/http-libretime` with the following contents.
|
||||
|
||||
```
|
||||
auth required pam_sss.so
|
||||
|
@ -113,68 +113,3 @@ general:
|
|||
```
|
||||
|
||||
You should now be able to use your FreeIPA credentials to log in to LibreTime.
|
||||
|
||||
## Setup Header Authentication
|
||||
|
||||
If you have an SSO system that supports trusted SSO header authentication such as [Authelia](https://www.authelia.com/),
|
||||
you can configure LibreTime to login users based on those trusted headers.
|
||||
|
||||
This allows users to only need to log in once on the SSO system and not need to log in again. It also allows LibreTime
|
||||
to indirectly support other authentication mechanisms such as OAuth2.
|
||||
|
||||
This ONLY affects Legacy/Legacy API auth and does NOT affect API V2 auth.
|
||||
|
||||
### Configure Headers
|
||||
|
||||
LibreTime needs to know what headers are sent, and what information is available to it. You can also
|
||||
setup a predefined group mapping so users are automatically granted the desired permissions.
|
||||
|
||||
This configuration is in `/etc/libretime/config.yml`. The following is an example configuration for an SSO service
|
||||
that does the following:
|
||||
|
||||
- Sends the username in the `Remote-User` HTTP header.
|
||||
- Sends the email in the `Remote-Email` HTTP header.
|
||||
- Sends the name in the `Remote-Name` HTTP header. Example `John Doe`
|
||||
- Sends the comma delimited groups in the `Remote-Groups` HTTP header. Example `group 1,lt-admin,group2`
|
||||
- Has an IP of `10.0.0.34` (not required). When not provided it is not checked.
|
||||
- Users with the `lt-host` group should get host privileges.
|
||||
- Users with the `lt-admin` group should get admin privileges.
|
||||
- Users with the `lt-pm` group should get program manager privileges.
|
||||
- Users with the `lt-superadmin` group should get super admin privileges.
|
||||
- All other users should get guest privileges.
|
||||
|
||||
```yml
|
||||
header_auth:
|
||||
user_header: Remote-User # This is the default and could be omitted
|
||||
groups_header: Remote-Groups # This is the default and could be omitted
|
||||
email_header: Remote-Email # This is the default and could be omitted
|
||||
name_header: Remote-Name # This is the default and could be omitted
|
||||
proxy_ip: 10.0.0.34
|
||||
group_map:
|
||||
host: lt-host
|
||||
program_manager: lt-pm
|
||||
admin: lt-admin
|
||||
superadmin: lt-superadmin
|
||||
```
|
||||
|
||||
If the `user_header` is not found in the request, users will be kicked to the login page
|
||||
with a message that their username/password is invalid and will not be able to log in. When `proxy_ip` is provided
|
||||
it will check that the request is coming from the correct proxy before doing the login. This prevents users who have
|
||||
internal network access from being able to login as whoever they want in LibreTime.
|
||||
|
||||
::: warning
|
||||
|
||||
If `proxy_ip` is not provided any user on the internal network can log in as any user in LibreTime.
|
||||
|
||||
:::
|
||||
|
||||
### Enable Header authentication
|
||||
|
||||
After everything is set up properly you can enable header auth in `config.yml`:
|
||||
|
||||
```yml
|
||||
general:
|
||||
auth: LibreTime_Auth_Adaptor_Header
|
||||
```
|
||||
|
||||
You should now be automatically logged into LibreTime when you click the `Login` button.
|
||||
|
|
|
@ -22,7 +22,7 @@ First, set the version you want to install:
|
|||
echo LIBRETIME_VERSION="{vars.version}" > .env
|
||||
</CodeBlock>
|
||||
|
||||
Download the docker compose files from the repository:
|
||||
Download the docker-compose files from the repository:
|
||||
|
||||
```bash
|
||||
# Load LIBRETIME_VERSION variable
|
||||
|
@ -106,16 +106,16 @@ You can find more details in the `docker-compose.yml` file or on the external se
|
|||
Next, run the following commands to setup the database:
|
||||
|
||||
```bash
|
||||
docker compose run --rm api libretime-api migrate
|
||||
docker-compose run --rm api libretime-api migrate
|
||||
```
|
||||
|
||||
Finally, start the services, and check that they're running using the following commands:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker-compose up -d
|
||||
|
||||
docker compose ps
|
||||
docker compose logs -f
|
||||
docker-compose ps
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
## Securing LibreTime
|
||||
|
|
|
@ -117,12 +117,12 @@ git checkout {vars.version}
|
|||
|
||||
## Run the installer
|
||||
|
||||
By default the installer will configure LibreTime to listen at the port `8080`. We recommend that you configure a [reverse proxy in front of LibreTime](./reverse-proxy.md) to secure the connection using HTTPS, and route the traffic from the ports `80`/`443` to the LibreTime server.
|
||||
By default the installer will configure LibreTime to listen at the port `80`, but this isn't the recommended way to install LibreTime. Instead you should configure a [reverse proxy in front of LibreTime](./reverse-proxy.md) to secure the connection using HTTPS, and route the traffic to the LibreTime server.
|
||||
|
||||
Install LibreTime with the following command, be sure to replace `https://libretime.example.org` with the public url of your installation:
|
||||
|
||||
```bash
|
||||
sudo ./install https://libretime.example.org
|
||||
sudo ./install --listen-port 8080 https://libretime.example.org
|
||||
```
|
||||
|
||||
:::caution
|
||||
|
@ -131,17 +131,23 @@ When upgrading be sure to run the installer using the same arguments you used du
|
|||
|
||||
:::
|
||||
|
||||
:::warning
|
||||
|
||||
To update the LibreTime nginx configuration file, for example to change the `--listen-port`, make sure to add the `--update-nginx` flag to allow overwriting the existing configuration file.
|
||||
|
||||
:::
|
||||
|
||||
If you need to change some configuration, the install script can be configured using flags or environment variables. Changing the listening port of LibreTime or whether you want to install some dependency by yourself, you could run the following:
|
||||
|
||||
```bash
|
||||
# Install LibreTime on your system with the following tweaks:
|
||||
# - don't install the liquidsoap package (remember to install liquidsoap yourself)
|
||||
# - set the listen port to 8081
|
||||
# - set the listen port to 8080
|
||||
# - don't run the PostgreSQL setup (remember to setup PostgreSQL yourself)
|
||||
sudo \
|
||||
LIBRETIME_PACKAGES_EXCLUDES='liquidsoap' \
|
||||
./install \
|
||||
--listen-port 8081 \
|
||||
--listen-port 8080 \
|
||||
--no-setup-postgresql \
|
||||
https://libretime.example.org
|
||||
```
|
||||
|
@ -150,7 +156,7 @@ You can persist the install configuration in a `.env` file next to the install s
|
|||
|
||||
```
|
||||
LIBRETIME_PACKAGES_EXCLUDES='liquidsoap'
|
||||
LIBRETIME_LISTEN_PORT='8081'
|
||||
LIBRETIME_LISTEN_PORT='8080'
|
||||
LIBRETIME_SETUP_POSTGRESQL=false
|
||||
LIBRETIME_PUBLIC_URL='https://libretime.example.org'
|
||||
```
|
||||
|
@ -230,9 +236,6 @@ server {
|
|||
|
||||
server_name libretime.example.org;
|
||||
|
||||
client_max_body_size 512M;
|
||||
client_body_timeout 300s;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
|
|
@ -36,6 +36,12 @@ flowchart LR
|
|||
|
||||
:::warning
|
||||
|
||||
By default, the installer configures nginx to listen on port 80, so you need to run the installer with the `--listen-port 8080` option to set the desired port.
|
||||
|
||||
:::
|
||||
|
||||
:::warning
|
||||
|
||||
The current input and output streams are Icecast based protocols and doesn't support being behind a reverse proxy. **Don't attempt** to [reverse proxy Icecast](#icecast) or the Liquidsoap harbor inputs.
|
||||
|
||||
You can [secure the Icecast output streams](#securing-the-icecast-output-streams) by adding an additional Icecast socket and reusing the TLS certificates used to secure LibreTime.
|
||||
|
@ -52,6 +58,8 @@ In this documentation, we will use `libretime.example.org` as domain name pointi
|
|||
|
||||
:::
|
||||
|
||||
If LibreTime is running on the same host as the reverse proxy, you need to change the LibreTime web server default listening port because the reverse proxy needs to listen on the `80`and `443` ports.
|
||||
|
||||
Be sure that your firewall and network allows communications from the reverse proxy to the services. You can use `ping`, `telnet` and `curl` to check that communication is working.
|
||||
|
||||
## Install a reverse proxy
|
||||
|
@ -76,9 +84,6 @@ server {
|
|||
listen 80;
|
||||
server_name libretime.example.org;
|
||||
|
||||
client_max_body_size 512M;
|
||||
client_body_timeout 300s;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
@ -127,9 +132,6 @@ server {
|
|||
listen 80;
|
||||
server_name libretime.example.org;
|
||||
|
||||
client_max_body_size 512M;
|
||||
client_body_timeout 300s;
|
||||
|
||||
if ($host = libretime.example.org) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
@ -141,9 +143,6 @@ server {
|
|||
listen 443 ssl; # managed by Certbot
|
||||
server_name libretime.example.org;
|
||||
|
||||
client_max_body_size 512M;
|
||||
client_body_timeout 300s;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/libretime.example.org/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/libretime.example.org/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
|
|
|
@ -17,12 +17,6 @@ Setting a higher bitrate for your output stream will only benefit your listeners
|
|||
|
||||
:::
|
||||
|
||||
:::caution
|
||||
|
||||
The liquidsoap playout handler version 1.4.3 shipped in Debian Bullseye and 1.4.1 shipped in Ubuntu Focal doesn't support AAC streaming output. If you want to stream AAC, you will need to replace the liquidsoap package with a version that supports AAC. See this [tutorial](./tutorials/setup-liquidsoap-aac-streaming.md) for more information.
|
||||
|
||||
:::
|
||||
|
||||
## Icecast
|
||||
|
||||
### UTF-8 metadata in Icecast MP3 streams
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
---
|
||||
title: How to update liquidsoap to support AAC streaming
|
||||
---
|
||||
|
||||
This tutorials walks you though the steps required to replace the liquidsoap package with a version that supports AAC streaming.
|
||||
|
||||
:::warning
|
||||
|
||||
Replacing the liquidsoap package has security implications, since this will remove the package from the system's package manager. This means that the package manager will not be able to update the liquidsoap package in the future. This includes backports of security fixes.
|
||||
|
||||
Libretime is NOT compatible with Liquidsoap 2.x at the time of this writing. Future versions of Libretime will support Liquidsoap 2.x which will render these instructions obsolete.
|
||||
|
||||
:::
|
||||
|
||||
:::info
|
||||
|
||||
Lets assume you already [installed LibreTime using the native OS installer](../install/install-using-the-installer.md). Execute the following commands as the libretime user.
|
||||
|
||||
:::
|
||||
|
||||
## 1. Obtain liquidsoap with AAC support
|
||||
|
||||
For Ubuntu 20.04 LTS ('focal'), use the following file:
|
||||
|
||||
```bash
|
||||
wget https://github.com/savonet/liquidsoap/releases/download/v1.4.4/liquidsoap-v1.4.4_1.4.4-ubuntu-focal-amd64-1_amd64.deb
|
||||
```
|
||||
|
||||
For Debian 11 ('Bullseye'), first enable non-free package source for libfdk-aac support:
|
||||
|
||||
```bash
|
||||
sudo apt install software-properties-common
|
||||
sudo apt-add-repository -c non-free
|
||||
```
|
||||
|
||||
Then use the following file:
|
||||
|
||||
```bash
|
||||
wget https://github.com/savonet/liquidsoap/releases/download/v1.4.4/liquidsoap-v1.4.4_1.4.4-debian-testing-amd64-1_amd64.deb
|
||||
```
|
||||
|
||||
## 2. Install and replace the liquidsoap package
|
||||
|
||||
Install the package using `apt`, then remove the old liquidsoap dependencies:
|
||||
|
||||
```bash
|
||||
sudo apt -y install ./liquidsoap-v1.4.4_1.4.4-*-amd64-1_amd64.deb
|
||||
sudo apt -y autoremove
|
||||
```
|
||||
|
||||
## 3. Configure LibreTime to use the new liquidsoap package
|
||||
|
||||
Nothing to do, this is a drop-in replacement. Just restart the libretime target once and then check the status page in the LibreTime web interface to see if the liquidsoap service is running.
|
||||
|
||||
```bash
|
||||
sudo systemctl restart libretime.target
|
||||
```
|
||||
|
||||
:::warning
|
||||
|
||||
If you want to update LibreTime in the future, you'll need to re-run the installer schript. This will replace the liquidsoap package with the version that doesn't support AAC streaming. Add `--packages-excludes liquidsoap` to the installer command to prevent this from happening.
|
||||
|
||||
:::
|
|
@ -54,7 +54,7 @@ liable to legal action.
|
|||
If you want to go down the commercial music route, check out the
|
||||
https://www.prsformusic.com and https://www.ppluk.com websites for UK licence
|
||||
details. In the USA, the https://www.soundexchange.com website currently quotes
|
||||
a 1000 (per Jan. 2024) dollar minimum annual fee for non-commercial webcasters, plus a usage fee
|
||||
a 500 dollar minimum annual fee for non-commercial webcasters, plus a usage fee
|
||||
above a certain number of listener hours, for the right to stream music
|
||||
recordings to listeners. See the websites of [ASCAP](https://www.ascap.com),
|
||||
[BMI](https://www.bmi.com) and [SESAC](https://www.sesac.com) for details of music
|
||||
|
|
|
@ -13,8 +13,8 @@ LibreTime development workflows follow the standardized [C4 development process]
|
|||
- [2.4. Development Process](https://rfc.zeromq.org/spec/42/#24-development-process)
|
||||
- `16.` Maintainers MAY NOT merge incorrect patches.
|
||||
- [2.5. Branches and Releases](https://rfc.zeromq.org/spec/42/#25-branches-and-releases)
|
||||
- `1.` The project SHALL have a development branch (`main`) that always holds the latest in-progress version and SHOULD always build. The project MAY have a bug fixes only branch (`stable-*`) that always holds the current stable version and SHOULD always build.
|
||||
- `3.` To make a stable release a Maintainer shall tag the repository. Stable releases SHALL always be released from the repository `main` or `stable-*` branches.
|
||||
- `1.` The project SHALL have a development branch (`main`) that always holds the latest in-progress version and SHOULD always build. The project MAY have a bug fixes only branch (`stable`) that always holds the current stable version and SHOULD always build.
|
||||
- `3.` To make a stable release a Maintainer shall tag the repository. Stable releases SHALL always be released from the repository `main` or `stable` branches.
|
||||
|
||||
## Contribute financially
|
||||
|
||||
|
|
|
@ -14,16 +14,16 @@ To setup a docker-compose development environment, run the following commands:
|
|||
# Clean and build
|
||||
make clean
|
||||
cp .env.dev .env
|
||||
DOCKER_BUILDKIT=1 docker compose build
|
||||
DOCKER_BUILDKIT=1 docker-compose build
|
||||
|
||||
# Setup
|
||||
make dev-certs
|
||||
docker compose run --rm legacy make build
|
||||
docker compose run --rm api libretime-api migrate
|
||||
docker-compose run --rm legacy make build
|
||||
docker-compose run --rm api libretime-api migrate
|
||||
|
||||
# Run
|
||||
docker compose up -d
|
||||
docker compose logs -f
|
||||
docker-compose up -d
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
:::info
|
||||
|
@ -33,7 +33,7 @@ You may also use the following `make clean dev` shortcut:
|
|||
```bash
|
||||
make clean dev
|
||||
|
||||
docker compose logs -f
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
:::
|
||||
|
|
|
@ -7,7 +7,7 @@ title: Development workflows
|
|||
LibreTime uses [Github pull requests to manage changes](https://docs.github.com/en/get-started/quickstart/contributing-to-projects). The workflow looks like this:
|
||||
|
||||
- [Create a fork of the project](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
|
||||
- Check out the `main` branch.
|
||||
- Check out the `main` branch. If you're making a minor or small documentation change you can check out the `stable` branch.
|
||||
- Create a new branch based on the checked out branch.
|
||||
- Work on your changes locally. Try to keep each commit small to make reviews easier.
|
||||
- Lint and test the codebase, for example using the `make lint` or `make test` commands inside the app folder you want to check.
|
||||
|
|
|
@ -6,17 +6,47 @@ title: Releases
|
|||
|
||||
This guide walks you through the steps required to release a new version of LibreTime.
|
||||
|
||||
### 1. Inspect the release pull request
|
||||
:::caution
|
||||
|
||||
A release pull request is maintained by [`release-please`](https://github.com/googleapis/release-please). `release-please` guesses the next version to release based on the commit history, and will generate a changelog for that release.
|
||||
This guide is still a work in progress, and doesn't cover every use cases. Depending on
|
||||
the version bump, some steps might be wrong. For example, in case of a patch release,
|
||||
the documentation requires different changes.
|
||||
|
||||
Once a release is desired, checkout the release branch:
|
||||
:::
|
||||
|
||||
Before releasing a new version, make sure linter don't fail and tests are passing.
|
||||
|
||||
Start by cleaning the repository and make sure you don't have uncommitted changes:
|
||||
|
||||
```
|
||||
git checkout main
|
||||
make clean
|
||||
git status
|
||||
```
|
||||
|
||||
Choose the next version based the our [versioning schema](#versioning-schema):
|
||||
|
||||
```bash
|
||||
# For a release on the main branch
|
||||
git checkout release-please--branches--main--components--libretime
|
||||
# For a release on the stable-4.x branch
|
||||
git checkout release-please--branches--stable-4.x--components--libretime
|
||||
export VERSION=3.0.0-beta.0
|
||||
```
|
||||
|
||||
Create a new `release-$VERSION` branch and release commit to prepare a release pull request:
|
||||
|
||||
```bash
|
||||
git checkout -b "release-$VERSION"
|
||||
export COMMIT_MESSAGE="chore: release $VERSION"
|
||||
git commit --allow-empty --message="$COMMIT_MESSAGE"
|
||||
```
|
||||
|
||||
### 1. Version bump
|
||||
|
||||
Write the new `$VERSION` to the VERSION file, and bump the python packages version:
|
||||
|
||||
```bash
|
||||
bash tools/bump-python-version.sh "$VERSION"
|
||||
|
||||
git add .
|
||||
git commit --fixup ":/$COMMIT_MESSAGE"
|
||||
```
|
||||
|
||||
### 2. Release note
|
||||
|
@ -42,16 +72,48 @@ Reset and clean the `docs/releases/unreleased.md` file for a future version.
|
|||
Commit the release note changes:
|
||||
|
||||
```bash
|
||||
git add docs/releases
|
||||
git commit -m "docs: add release note"
|
||||
git add .
|
||||
git commit --fixup ":/$COMMIT_MESSAGE"
|
||||
```
|
||||
|
||||
### 4. Merge the release pull request
|
||||
### 3. Create a new pull request
|
||||
|
||||
Push any changes that we previously made to the release branch:
|
||||
Squash the changes and open a pull request for others to review:
|
||||
|
||||
```bash
|
||||
git push
|
||||
git rebase --autosquash --interactive main
|
||||
```
|
||||
|
||||
Once the pull request CI succeeded and everything is ready, merge the release pull request. `release-please` will create a tag and a release, which will trigger the final release pipeline that will upload the tarball as release assets.
|
||||
Merge the pull request when it's reviewed and ready.
|
||||
|
||||
### 4. Create and push a tag
|
||||
|
||||
Pull the merged release commit:
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git pull upstream main
|
||||
```
|
||||
|
||||
Make sure `HEAD` is the previously merged release commit and tag it with the new version:
|
||||
|
||||
```bash
|
||||
git show --quiet
|
||||
|
||||
git tag -a -m "$VERSION" "$VERSION"
|
||||
```
|
||||
|
||||
Generate the changelog for the newly tagged version:
|
||||
|
||||
```bash
|
||||
make changelog
|
||||
|
||||
git add .
|
||||
git commit -m "chore: generate changelog for $VERSION"
|
||||
```
|
||||
|
||||
Push the tag upstream to finalize the release process:
|
||||
|
||||
```bash
|
||||
git push upstream main --follow-tags
|
||||
```
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
title: LibreTime 3.2.0
|
||||
title: Unreleased
|
||||
---
|
||||
|
||||
import ReleaseHead from './\_release-head.md';
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
---
|
||||
title: LibreTime 4.0.0
|
||||
---
|
||||
|
||||
import ReleaseHead from './\_release-head.md';
|
||||
|
||||
<ReleaseHead date='2024-01-07' version='4.0.0'/>
|
||||
|
||||
## :sparkling_heart: Contributors
|
||||
|
||||
The LibreTime project wants to thank the following contributors for authoring PRs to this release:
|
||||
|
||||
- @jooola
|
||||
- @maxtimbo
|
||||
- @mp3butcher
|
||||
|
||||
## :rocket: Features
|
||||
|
||||
- default system output is now `pulseaudio` ([#2842](https://github.com/libretime/libretime/issues/2842)) ([083ee3f](https://github.com/libretime/libretime/commit/083ee3f1dd74441e288b4d63178ae9cea12ba286)), closes [#2542](https://github.com/libretime/libretime/issues/2542)
|
||||
- disable uvicorn worker lifespan ([#2845](https://github.com/libretime/libretime/issues/2845)) ([8743c84](https://github.com/libretime/libretime/commit/8743c84d0f007a5430e9059c197a261e613cc642))
|
||||
- **installer:** add the `--storage-path` flag ([#2865](https://github.com/libretime/libretime/issues/2865)) ([5b23852](https://github.com/libretime/libretime/commit/5b23852f8d144f0c7cdeb62831f7b1a27872b40e))
|
||||
- **installer:** change default listen port to 8080 ([#2852](https://github.com/libretime/libretime/issues/2852)) ([f72b7f9](https://github.com/libretime/libretime/commit/f72b7f9c9727800a9d77d64c540c12f272bb0ae3))
|
||||
- **installer:** remove the `--update-nginx` flag ([#2851](https://github.com/libretime/libretime/issues/2851)) ([35d7eac](https://github.com/libretime/libretime/commit/35d7eace13c2b9667fdb41fec0788118e0c5e63f))
|
||||
- **playout:** configure device for alsa and pulseaudio system outputs ([#2654](https://github.com/libretime/libretime/issues/2654)) ([06af18b](https://github.com/libretime/libretime/commit/06af18b84e7dfaad95e3b55dda22ec1ddad27050))
|
||||
- rewrite cloud-init config ([#2853](https://github.com/libretime/libretime/issues/2853)) ([8406d52](https://github.com/libretime/libretime/commit/8406d520d7a7bea4060be8a00e360bcf413cb2d5))
|
||||
- run python in optimized mode ([#2874](https://github.com/libretime/libretime/issues/2874)) ([3f7fc99](https://github.com/libretime/libretime/commit/3f7fc99b6b343fbc8df319d8130ba8247aea96d8))
|
||||
- the `general.secret_key` configuration field is now required ([#2841](https://github.com/libretime/libretime/issues/2841)) ([0d2d1a2](https://github.com/libretime/libretime/commit/0d2d1a26731a2b41ce5e574ed6de9950eaae4153)), closes [#2426](https://github.com/libretime/libretime/issues/2426)
|
||||
- use nginx to serve media files ([#2860](https://github.com/libretime/libretime/issues/2860)) ([4603c17](https://github.com/libretime/libretime/commit/4603c1759f29b8a1adb3e83d610ca00e778d76bd))
|
||||
|
||||
## :bug: Bug fixes
|
||||
|
||||
- add parent function name in setValue exception ([#2777](https://github.com/libretime/libretime/issues/2777)) ([c764a5a](https://github.com/libretime/libretime/commit/c764a5a648ac6cf6c1f63cd9be6de9fe07c40988))
|
||||
- **api:** ensure non ascii paths are handled by X-Accel-Redirect ([#2861](https://github.com/libretime/libretime/issues/2861)) ([0ce63f3](https://github.com/libretime/libretime/commit/0ce63f3bf0448580170024cdde96ee351ee5c358))
|
||||
- **api:** enum schema description ([#2803](https://github.com/libretime/libretime/issues/2803)) ([976b70e](https://github.com/libretime/libretime/commit/976b70ed32a0e774cc0b72b8332372be32799ed1))
|
||||
- **api:** let nginx handle the media file content type ([#2862](https://github.com/libretime/libretime/issues/2862)) ([72268ad](https://github.com/libretime/libretime/commit/72268ad9bb1a96b24efda7143b9371d6fd98ca03))
|
||||
- **api:** move gunicorn worker config to python file ([#2854](https://github.com/libretime/libretime/issues/2854)) ([43221d9](https://github.com/libretime/libretime/commit/43221d9d7f34ba98a14db9906e350cb494a86b25))
|
||||
- **api:** paths with question marks chars are handled by X-Accel-Redirect ([#2875](https://github.com/libretime/libretime/issues/2875)) ([b2c1ceb](https://github.com/libretime/libretime/commit/b2c1ceb89fafc76f18ec650d19ec0ff03e4a20b0))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.42.1 (main) ([#2765](https://github.com/libretime/libretime/issues/2765)) ([8ae4dce](https://github.com/libretime/libretime/commit/8ae4dce9e7c013c1f66f1b4d5da4a8c91d3419b7))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.43.2 (main) ([#2848](https://github.com/libretime/libretime/issues/2848)) ([62e5f4d](https://github.com/libretime/libretime/commit/62e5f4dfbb76ab1919c4905570cc34274c685cef))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.45.1 (main) ([#2855](https://github.com/libretime/libretime/issues/2855)) ([6f84328](https://github.com/libretime/libretime/commit/6f8432838058be6ef1cfa7858f17b8272929896e))
|
||||
- **deps:** update dependency friendsofphp/php-cs-fixer to <3.46.1 (main) ([#2868](https://github.com/libretime/libretime/issues/2868)) ([4827dbc](https://github.com/libretime/libretime/commit/4827dbce711262e90238bb3b6c0a35b1ce3d6877))
|
||||
- **legacy:** allow uploading opus files ([#2804](https://github.com/libretime/libretime/issues/2804)) ([f252a16](https://github.com/libretime/libretime/commit/f252a16637e113ceb1dd340fb7aad31af9c23ff0))
|
||||
- **legacy:** declare previously undeclared variable ([#2793](https://github.com/libretime/libretime/issues/2793)) ([e2cfbf4](https://github.com/libretime/libretime/commit/e2cfbf4c038f28874a206df5805f04f69a40647b))
|
||||
- **legacy:** ensure last played criteria works with never played files ([#2840](https://github.com/libretime/libretime/issues/2840)) ([24ee383](https://github.com/libretime/libretime/commit/24ee3830c23f7147f82febe3d3c6743d5ae8d4e6))
|
||||
- **playout:** increase file download chunk size to 8192 bytes ([#2863](https://github.com/libretime/libretime/issues/2863)) ([7ed1be1](https://github.com/libretime/libretime/commit/7ed1be1816abef20b9ae59a8c66a9e48a34f37c5))
|
||||
- **playout:** remove empty file when the download request failed ([#2864](https://github.com/libretime/libretime/issues/2864)) ([2facbfa](https://github.com/libretime/libretime/commit/2facbfaff23d4df0e7531b82f04f932bb2c4c9a4))
|
||||
- **worker:** unbound variable when episode url returns HTTP 404 ([#2844](https://github.com/libretime/libretime/issues/2844)) ([3f39689](https://github.com/libretime/libretime/commit/3f396895e588e62183e01d17927d9bdbea512ee0))
|
||||
|
||||
## :arrow_up: Upgrading
|
||||
|
||||
### Installer changes
|
||||
|
||||
The installer received a few breaking changes:
|
||||
|
||||
- The default listen port for the installer is now `8080`. We recommend that you put a reverse proxy in front of LibreTime with your customizations.
|
||||
- The `--update-nginx` flag was removed from the installer. The nginx configuration deployed by the installer will now always be overwritten. Again, we recommend that you put a reverse proxy in front of LibreTime with your customizations.
|
||||
- The media file serving is now handled by Nginx instead of the API service. The configuration `storage.path` field is now used in the Nginx configuration, so make sure that the Nginx configuration file has the right storage path. To update the Nginx configuration file with the installer, you can use the `--storage-path` option to specify your custom storage path.
|
||||
|
||||
### The `general.secret_key` configuration field is required
|
||||
|
||||
The `general.secret_key` field in the [configuration file](../admin-manual/configuration.md#general) is now **required**, to prevent reusing the `general.api_key` for cryptographic usage.
|
||||
|
||||
### The `stream.outputs.system[].kind` configuration field now defaults to `pulseaudio`
|
||||
|
||||
The `stream.outputs.system[].kind` field in the [configuration file](../admin-manual/configuration.md#general) default value changed from `alsa` to `pulseaudio`. Make sure to update your configuration file if you rely on the default system output.
|
|
@ -1,31 +0,0 @@
|
|||
---
|
||||
title: LibreTime 4.1.0
|
||||
---
|
||||
|
||||
import ReleaseHead from './\_release-head.md';
|
||||
|
||||
<ReleaseHead date='2024-05-05' version='4.1.0'/>
|
||||
|
||||
## :sparkling_heart: Contributors
|
||||
|
||||
The LibreTime project wants to thank the following contributors for authoring PRs to this release:
|
||||
|
||||
- @caveman99
|
||||
- @jooola
|
||||
- @kmahelona
|
||||
- @mp3butcher
|
||||
- @paddatrapper
|
||||
|
||||
## :rocket: Features
|
||||
|
||||
Please see the [changelog](https://github.com/libretime/libretime/blob/main/CHANGELOG.md#410-2024-05-05).
|
||||
|
||||
## :bug: Bug fixes
|
||||
|
||||
Please see the [changelog](https://github.com/libretime/libretime/blob/main/CHANGELOG.md#410-2024-05-05).
|
||||
|
||||
## :arrow_up: Upgrading
|
||||
|
||||
### Replay gain modifier preference
|
||||
|
||||
The `replay_gain_modifier` preference is now stored as system preference. Please check and save the replay gain modifier preference manually to make sure the preference is up to date and usable.
|
|
@ -1,25 +0,0 @@
|
|||
---
|
||||
title: LibreTime 4.2.0
|
||||
---
|
||||
|
||||
import ReleaseHead from './\_release-head.md';
|
||||
|
||||
<ReleaseHead date='2024-06-22' version='4.2.0'/>
|
||||
|
||||
## :sparkling_heart: Contributors
|
||||
|
||||
The LibreTime project wants to thank the following contributors for authoring PRs to this release:
|
||||
|
||||
- @conet
|
||||
- @dakriy
|
||||
- @jooola
|
||||
- @paddatrapper
|
||||
- @rjhelms
|
||||
|
||||
## :rocket: Features
|
||||
|
||||
Please see the [changelog](https://github.com/libretime/libretime/blob/main/CHANGELOG.md#420-2024-06-22).
|
||||
|
||||
## :bug: Bug fixes
|
||||
|
||||
Please see the [changelog](https://github.com/libretime/libretime/blob/main/CHANGELOG.md#420-2024-06-22).
|
|
@ -30,4 +30,3 @@ New releases target the current stable distributions release, and development sh
|
|||
| Versions | | | | |
|
||||
| 3.0.x | deprecated | deprecated | recommended | recommended |
|
||||
| 3.1.x | | | recommended | recommended |
|
||||
| 4.0.x | | | recommended | recommended |
|
||||
|
|
|
@ -12,12 +12,8 @@ The LibreTime project wants to thank the following contributors for authoring PR
|
|||
|
||||
## :rocket: Features
|
||||
|
||||
Please see the [changelog](https://github.com/libretime/libretime/blob/main/CHANGELOG.md).
|
||||
|
||||
## :bug: Bug fixes
|
||||
|
||||
Please see the [changelog](https://github.com/libretime/libretime/blob/main/CHANGELOG.md).
|
||||
|
||||
## :fire: Deprecation and removal
|
||||
|
||||
## :arrow_up: Before upgrading
|
||||
|
|
|
@ -37,7 +37,6 @@ Smart blocks are automatically filled with media files from the LibreTime librar
|
|||
To create a smart block, click the **Smartblocks** button on the left sidebar, and select **New** from the toolbar. Like a playlist, smart blocks can have a title and description, which you can edit. This helps you find relevant smart blocks in searches.
|
||||
|
||||
Fill out the smart block's **Name**, **Search Criteria**, and **Limit to** sections. The search criteria can be any one of LibreTime's metadata categories, such as **Title**, **Creator** or **Genre**. The modifier depends on whether the metadata in question contains letters or numbers. For example, **Title** has modifiers including _contains_ and _starts with_, whereas the modifiers for **BPM** include _is greater than_ and _is in the range_.
|
||||
To filter tracks using today's date information, use the `now{}` macro. Format characters are listed in the [php documentation](https://www.php.net/manual/en/datetime.format.php). For example, to filter to tracks with a **Title** that ends in `Instrumental Jan 2024` where `Jan 2024` is the current month and year, add a criteria for **Title** with a modifier of **ends with** and a value of `Instrumental now{M Y}`.
|
||||
|
||||
If you have a large number of files which meet the criteria that you specify, you may wish to limit the duration of the smart block using the **Limit to** field, so that it fits within the show you have in mind. Select **hours**, **minutes** or **items** from the drop-down menu, and click the **Generate** button again, if it's a static smart block. Then click the **Save** button.
|
||||
|
||||
|
|
|
@ -79,8 +79,6 @@ indicator.
|
|||
| Add Autoloading Playlist? | If checked, allows for the following options |
|
||||
| Select Playlist | Select the playlist the show will autofill from (shows autofill exactly one hour before air). If you wish to use a smartblock you must add it to a playlist and then select that playlist. This can be used to auto schedule new podcast episodes to air. |
|
||||
| Repeat Playlist Until Show Is Full | If checked, the playlist will be added to the show multiple times until the slot is full. Useful for applying a one-hour music playlist made up of smartblocks to a two-hour show. |
|
||||
| Select Intro Playlist | Select the playlist to replace the global intro playlist from settings. If you wish to use a smartblock you must add it to a playlist and then select that playlist. |
|
||||
| Select Outro Playlist | Select the playlist to replace the global outro playlist from settings. If you wish to use a smartblock you must add it to a playlist and then select that playlist. |
|
||||
| _Live Stream Input_ | |
|
||||
| Use LibreTime/Custom Authentication | |
|
||||
| Show Source | |
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue