Compare commits
156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f28790476 | ||
|
|
c8dae95eff | ||
|
|
547a084c7e | ||
|
|
493677ff81 | ||
|
|
6b430ba271 | ||
|
|
bc6924d303 | ||
|
|
02b00f9146 | ||
|
|
e807ed5870 | ||
|
|
f08944253b | ||
|
|
792d6584b9 | ||
|
|
7b45d01462 | ||
|
|
5c17b2966a | ||
|
|
0c54f29446 | ||
|
|
9c3cf39fdd | ||
|
|
d2fb229685 | ||
|
|
f55bc6682f | ||
|
|
6857286b42 | ||
|
|
c0d5bd2048 | ||
|
|
666e5d7904 | ||
|
|
3613407ac5 | ||
|
|
c32dceb4d4 | ||
|
|
5573e78364 | ||
|
|
807a19889f | ||
|
|
df7242de91 | ||
|
|
d62426f168 | ||
|
|
1714cf3417 | ||
|
|
1080c917be | ||
|
|
8eb3164878 | ||
|
|
915c5442d1 | ||
|
|
be0360d5e8 | ||
|
|
4d6ae6ffdd | ||
|
|
ecee6f415b | ||
|
|
47680f936b | ||
|
|
2d7181105d | ||
|
|
9bdc75524b | ||
|
|
2f6ceb4949 | ||
|
|
cd933aa35f | ||
|
|
138738075b | ||
|
|
2ee57f9b0d | ||
|
|
5a5655b790 | ||
|
|
b88d1e6820 | ||
|
|
19d2864c34 | ||
|
|
29e3a17f26 | ||
|
|
252e9f736f | ||
|
|
5d08988dda | ||
|
|
47ca4246aa | ||
|
|
f8338d4f00 | ||
|
|
5cf6032c90 | ||
|
|
8d8b77412d | ||
|
|
fd9114e7e2 | ||
|
|
a3fba2f8f7 | ||
|
|
e2b671f1b2 | ||
|
|
2a35fbe29e | ||
|
|
81cde72fd0 | ||
|
|
bf9ffba4f7 | ||
|
|
c975d8b943 | ||
|
|
2730f91872 | ||
|
|
97ca85e155 | ||
|
|
39bb4c5871 | ||
|
|
bdceb90c59 | ||
|
|
8bd1b5228c | ||
|
|
a009e95afd | ||
|
|
32aafb3572 | ||
|
|
b577783cf0 | ||
|
|
aa7b872a14 | ||
|
|
c6f7f57776 | ||
|
|
106ad08cd2 | ||
|
|
0341ca1b6a | ||
|
|
7581ea55db | ||
|
|
fc9cee38d8 | ||
|
|
b175e4128d | ||
|
|
97b07798b0 | ||
|
|
112fcd206d | ||
|
|
11d1f56062 | ||
|
|
bd840d4638 | ||
|
|
c3d393f214 | ||
|
|
f88fc0ca1a | ||
|
|
fb8d8242ab | ||
|
|
f2a3dfd700 | ||
|
|
85f9863e0a | ||
|
|
83572701f4 | ||
|
|
fa7d7e9187 | ||
|
|
f818cde32c | ||
|
|
9da93cd887 | ||
|
|
026e7ea32a | ||
|
|
9659d19718 | ||
|
|
50d35c9677 | ||
|
|
4260e78861 | ||
|
|
7342ae2e33 | ||
|
|
35dbc1a90c | ||
|
|
c7a4355153 | ||
|
|
33a84a8ca2 | ||
|
|
1d04490ed3 | ||
|
|
4a30c2d79c | ||
|
|
83072d6b9c | ||
|
|
c779fc37eb | ||
|
|
e08c13ad7e | ||
|
|
2c82a6b2e0 | ||
|
|
3929f17aef | ||
|
|
ee39af3419 | ||
|
|
3882a5a263 | ||
|
|
ac06088948 | ||
|
|
a757eebfbb | ||
|
|
2be4f89555 | ||
|
|
4a5c7d8261 | ||
|
|
f591193dda | ||
|
|
434869900e | ||
|
|
2aed7378cc | ||
|
|
71cd6e6248 | ||
|
|
c83294916a | ||
|
|
603bbe0afd | ||
|
|
c361e235eb | ||
|
|
8a59493d96 | ||
|
|
7ef86cbf9f | ||
|
|
c9530118a4 | ||
|
|
878d9abeb7 | ||
|
|
2d705efe1c | ||
|
|
aeaef85507 | ||
|
|
ebae25d175 | ||
|
|
5ad1a01d7a | ||
|
|
8f84e1befd | ||
|
|
9975905faf | ||
|
|
233184568c | ||
|
|
59da778009 | ||
|
|
108ce95b7c | ||
|
|
86e9ed5f3a | ||
|
|
fbecb05bf4 | ||
|
|
4983703375 | ||
|
|
3856224df9 | ||
|
|
6d4bedfc56 | ||
|
|
bea821f194 | ||
|
|
4e276256c0 | ||
|
|
d0f9062b56 | ||
|
|
b9cc036703 | ||
|
|
4e9b88559b | ||
|
|
3452682a42 | ||
|
|
9262b24504 | ||
|
|
a5fa43b526 | ||
|
|
8681a3d74c | ||
|
|
f9c4d88b12 | ||
|
|
799032505e | ||
|
|
c8f174ac92 | ||
|
|
047e169f3e | ||
|
|
687327c9e8 | ||
|
|
26dc37bd76 | ||
|
|
c693e4aa64 | ||
|
|
acab731fef | ||
|
|
7e4ba3cb72 | ||
|
|
172c4d9c7d | ||
|
|
bd5f6cbc7b | ||
|
|
6fcd1c734b | ||
|
|
eca097dbfb | ||
|
|
51ffafa011 | ||
|
|
8dca602346 | ||
|
|
0ed24f3a05 | ||
|
|
e25e0030e7 |
12
.github/FUNDING.yml
vendored
12
.github/FUNDING.yml
vendored
@@ -1,12 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
|
||||||
patreon: # Replace with a single Patreon username
|
|
||||||
open_collective: # Replace with a single Open Collective username
|
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
||||||
liberapay: MaxK
|
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
|
||||||
otechie: # Replace with a single Otechie username
|
|
||||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
||||||
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -18,5 +18,9 @@ about: Create a bug report
|
|||||||
<!-- Paste the output of "mpd --version" here -->
|
<!-- Paste the output of "mpd --version" here -->
|
||||||
|
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
<!-- Paste your MPD configuration here -->
|
||||||
|
|
||||||
|
|
||||||
## Log
|
## Log
|
||||||
<!-- Paste relevant portions of the log file here (--verbose) -->
|
<!-- Paste relevant portions of the log file here (--verbose) -->
|
||||||
|
|||||||
9
.github/ISSUE_TEMPLATE/question.md
vendored
9
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
name: Question
|
|
||||||
about: Ask a question about MPD
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- Before you ask a question on GitHub, please read MPD's
|
|
||||||
documentation. A copy is available at
|
|
||||||
https://www.musicpd.org/doc/html/ -->
|
|
||||||
## Question
|
|
||||||
31
.github/workflows/build.yml
vendored
31
.github/workflows/build.yml
vendored
@@ -12,7 +12,7 @@ on:
|
|||||||
- 'win32/**'
|
- 'win32/**'
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- actions
|
- v0.23.x
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'android/**'
|
- 'android/**'
|
||||||
@@ -24,6 +24,7 @@ on:
|
|||||||
- 'win32/**'
|
- 'win32/**'
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- v0.23.x
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-linux:
|
build-linux:
|
||||||
@@ -40,7 +41,8 @@ jobs:
|
|||||||
key: linux
|
key: linux
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt install -y --no-install-recommends \
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y --no-install-recommends \
|
||||||
g++-10 libfmt-dev libboost-dev \
|
g++-10 libfmt-dev libboost-dev \
|
||||||
libgtest-dev \
|
libgtest-dev \
|
||||||
libpcre2-dev \
|
libpcre2-dev \
|
||||||
@@ -72,19 +74,30 @@ jobs:
|
|||||||
libgcrypt20-dev
|
libgcrypt20-dev
|
||||||
|
|
||||||
- name: Full Build
|
- name: Full Build
|
||||||
|
uses: BSFishy/meson-build@v1.0.3
|
||||||
|
with:
|
||||||
|
action: build
|
||||||
|
directory: output/full
|
||||||
|
setup-options: -Ddocumentation=disabled -Dtest=true -Dsystemd=enabled -Dpcre=enabled
|
||||||
|
options: --verbose
|
||||||
|
meson-version: 0.56.0
|
||||||
|
|
||||||
|
- name: Unit Tests
|
||||||
uses: BSFishy/meson-build@v1.0.3
|
uses: BSFishy/meson-build@v1.0.3
|
||||||
with:
|
with:
|
||||||
action: test
|
action: test
|
||||||
directory: output/full
|
directory: output/full
|
||||||
setup-options: -Ddocumentation=disabled -Dtest=true -Dsystemd=enabled -Dpcre=enabled
|
setup-options: -Ddocumentation=disabled -Dtest=true -Dsystemd=enabled -Dpcre=enabled
|
||||||
|
options: --verbose
|
||||||
meson-version: 0.56.0
|
meson-version: 0.56.0
|
||||||
|
|
||||||
- name: Mini Build
|
- name: Mini Build
|
||||||
uses: BSFishy/meson-build@v1.0.3
|
uses: BSFishy/meson-build@v1.0.3
|
||||||
with:
|
with:
|
||||||
action: test
|
action: build
|
||||||
directory: output/mini
|
directory: output/mini
|
||||||
setup-options: -Dbuildtype=minsize -Dauto_features=disabled -Dtest=true -Ddaemon=false -Dinotify=false -Depoll=false -Deventfd=false -Dsignalfd=false -Dtcp=false -Ddsd=false -Ddatabase=false -Dneighbor=false -Dcue=false -Dfifo=false -Dhttpd=false -Dpipe=false -Drecorder=false -Dsnapcast=false
|
setup-options: -Dbuildtype=minsize -Dauto_features=disabled -Dtest=true -Ddaemon=false -Dinotify=false -Depoll=false -Deventfd=false -Dsignalfd=false -Dtcp=false -Ddsd=false -Ddatabase=false -Dneighbor=false -Dcue=false -Dfifo=false -Dhttpd=false -Dpipe=false -Drecorder=false -Dsnapcast=false
|
||||||
|
options: --verbose
|
||||||
meson-version: 0.56.0
|
meson-version: 0.56.0
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
@@ -123,10 +136,20 @@ jobs:
|
|||||||
wavpack \
|
wavpack \
|
||||||
libmpdclient
|
libmpdclient
|
||||||
|
|
||||||
- name: Meson Build
|
- name: Build
|
||||||
|
uses: BSFishy/meson-build@v1.0.3
|
||||||
|
with:
|
||||||
|
action: build
|
||||||
|
directory: output
|
||||||
|
setup-options: -Ddocumentation=disabled -Dtest=true
|
||||||
|
options: --verbose
|
||||||
|
meson-version: 0.56.0
|
||||||
|
|
||||||
|
- name: Unit Tests
|
||||||
uses: BSFishy/meson-build@v1.0.3
|
uses: BSFishy/meson-build@v1.0.3
|
||||||
with:
|
with:
|
||||||
action: test
|
action: test
|
||||||
directory: output
|
directory: output
|
||||||
setup-options: -Ddocumentation=disabled -Dtest=true
|
setup-options: -Ddocumentation=disabled -Dtest=true
|
||||||
|
options: --verbose
|
||||||
meson-version: 0.56.0
|
meson-version: 0.56.0
|
||||||
|
|||||||
44
NEWS
44
NEWS
@@ -1,3 +1,47 @@
|
|||||||
|
ver 0.23.8 (2022/07/09)
|
||||||
|
* storage
|
||||||
|
- curl: fix crash if web server does not understand WebDAV
|
||||||
|
* input
|
||||||
|
- cdio_paranoia: fix crash if no drive was found
|
||||||
|
- cdio_paranoia: faster cancellation
|
||||||
|
- cdio_paranoia: don't scan for replay gain tags
|
||||||
|
- pipewire: fix playback of very short tracks
|
||||||
|
- pipewire: drop all buffers before manual song change
|
||||||
|
- pipewire: fix stuttering after manual song change
|
||||||
|
- snapcast: fix busy loop while paused
|
||||||
|
- snapcast: fix stuttering after resuming playback
|
||||||
|
* mixer
|
||||||
|
- better error messages
|
||||||
|
- alsa: fix setting volume before playback starts
|
||||||
|
- pipewire: fix crash bug
|
||||||
|
- pipewire: fix volume change events with PipeWire 0.3.53
|
||||||
|
- pipewire: don't force initial volume=100%
|
||||||
|
* support libfmt 9
|
||||||
|
|
||||||
|
ver 0.23.7 (2022/05/09)
|
||||||
|
* database
|
||||||
|
- upnp: support pupnp 1.14
|
||||||
|
* decoder
|
||||||
|
- ffmpeg: fix HLS seeking
|
||||||
|
- opus: fix missing song length on high-latency files
|
||||||
|
* output
|
||||||
|
- shout: require at least libshout 2.4.0
|
||||||
|
* mixer
|
||||||
|
- pipewire: fix volume restore
|
||||||
|
- software: update volume of disabled outputs
|
||||||
|
* support libiconv
|
||||||
|
|
||||||
|
ver 0.23.6 (2022/03/14)
|
||||||
|
* protocol
|
||||||
|
- support filename "cover.webp" for "albumart" command
|
||||||
|
- support "readcomments" and "readpicture" on CUE tracks
|
||||||
|
* decoder
|
||||||
|
- ffmpeg: fix end-of-file check (update stuck at empty files)
|
||||||
|
- opus: fix "readpicture" on Opus files
|
||||||
|
* output
|
||||||
|
- pipewire: fix crash bug if setting volume before playback starts
|
||||||
|
- wasapi: fix resume after pause
|
||||||
|
|
||||||
ver 0.23.5 (2021/12/01)
|
ver 0.23.5 (2021/12/01)
|
||||||
* protocol
|
* protocol
|
||||||
- support relative offsets for "searchadd"
|
- support relative offsets for "searchadd"
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.musicpd"
|
package="org.musicpd"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
android:versionCode="65"
|
android:versionCode="66"
|
||||||
android:versionName="0.23.5">
|
android:versionName="0.23.7">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
|
||||||
|
|
||||||
@@ -17,10 +17,9 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
|
||||||
|
|
||||||
<application android:allowBackup="true"
|
<application android:allowBackup="true"
|
||||||
|
android:debuggable="true"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:icon="@drawable/icon"
|
android:icon="@drawable/icon"
|
||||||
android:banner="@drawable/icon"
|
android:banner="@drawable/icon"
|
||||||
@@ -43,7 +42,6 @@
|
|||||||
<receiver android:name=".Receiver">
|
<receiver android:name=".Receiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
<action android:name="android.intent.action.HEADSET_PLUG" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
<service android:name=".Main" android:process=":main"/>
|
<service android:name=".Main" android:process=":main"/>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ android_abi = sys.argv[3]
|
|||||||
configure_args = sys.argv[4:]
|
configure_args = sys.argv[4:]
|
||||||
|
|
||||||
if not os.path.isfile(os.path.join(sdk_path, 'tools', 'android')):
|
if not os.path.isfile(os.path.join(sdk_path, 'tools', 'android')):
|
||||||
print("SDK not found in", ndk_path, file=sys.stderr)
|
print("SDK not found in", sdk_path, file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if not os.path.isdir(ndk_path):
|
if not os.path.isdir(ndk_path):
|
||||||
|
|||||||
54
android/gdb.sh
Executable file
54
android/gdb.sh
Executable file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# This script need the following modification in ANDROID_NDK in order to attach
|
||||||
|
# to the good :main pid
|
||||||
|
#--- a/prebuilt/linux-x86_64/bin/ndk-gdb.py
|
||||||
|
#+++ b/prebuilt/linux-x86_64/bin/ndk-gdb.py
|
||||||
|
#@@ -669,7 +669,7 @@
|
||||||
|
# log("Sleeping for {} seconds.".format(args.delay))
|
||||||
|
# time.sleep(args.delay)
|
||||||
|
#
|
||||||
|
#- pids = gdbrunner.get_pids(device, pkg_name)
|
||||||
|
#+ pids = gdbrunner.get_pids(device, pkg_name + ":main")
|
||||||
|
# if len(pids) == 0:
|
||||||
|
# error("Failed to find running process '{}'".format(pkg_name))
|
||||||
|
# if len(pids) > 1:
|
||||||
|
|
||||||
|
SCRIPT_PATH=$(dirname $0)
|
||||||
|
BUILD_PATH="`pwd`"
|
||||||
|
TMP_PATH="$BUILD_PATH/gdb"
|
||||||
|
NDK_GDB_ARGS="--force"
|
||||||
|
ANDROID_NDK=$1
|
||||||
|
|
||||||
|
if [ ! -f $ANDROID_NDK/source.properties ];then
|
||||||
|
echo "usage: $0 ANDROID_NDK"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f $BUILD_PATH/libmpd.so ];then
|
||||||
|
echo "This script need to be executed from the android build directory"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "$TMP_PATH"
|
||||||
|
mkdir -p "$TMP_PATH"
|
||||||
|
|
||||||
|
ANDROID_MANIFEST="$SCRIPT_PATH/AndroidManifest.xml"
|
||||||
|
ABI=`ls "$BUILD_PATH/android/apk/apk/lib" --sort=time | head -n 1`
|
||||||
|
|
||||||
|
if [ ! -f "$ANDROID_MANIFEST" -o "$ABI" = "" ]; then
|
||||||
|
echo "Invalid manifest/ABI, did you try building first ?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$TMP_PATH"/jni
|
||||||
|
touch "$TMP_PATH"/jni/Android.mk
|
||||||
|
echo "APP_ABI := $ABI" > "$TMP_PATH"/jni/Application.mk
|
||||||
|
|
||||||
|
DEST=obj/local/$ABI
|
||||||
|
mkdir -p "$TMP_PATH/$DEST"
|
||||||
|
|
||||||
|
cp "$BUILD_PATH/libmpd.so" "$TMP_PATH/$DEST"
|
||||||
|
cp "$ANDROID_MANIFEST" "$TMP_PATH"
|
||||||
|
|
||||||
|
(cd "$TMP_PATH" && bash $ANDROID_NDK/ndk-gdb $NDK_GDB_ARGS)
|
||||||
@@ -24,14 +24,13 @@ import android.app.Notification;
|
|||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.bluetooth.BluetoothDevice;
|
|
||||||
import android.bluetooth.BluetoothClass;
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
|
import android.media.AudioManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
@@ -200,24 +199,14 @@ public class Main extends Service implements Runnable {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(Intent.ACTION_HEADSET_PLUG);
|
filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
|
||||||
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
|
|
||||||
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
|
||||||
registerReceiver(new BroadcastReceiver() {
|
registerReceiver(new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (!mPauseOnHeadphonesDisconnect) {
|
if (!mPauseOnHeadphonesDisconnect)
|
||||||
return;
|
return;
|
||||||
}
|
if (intent.getAction() == AudioManager.ACTION_AUDIO_BECOMING_NOISY)
|
||||||
|
|
||||||
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
|
|
||||||
if (intent.hasExtra("state") && intent.getIntExtra("state", 0) == 0)
|
|
||||||
pause();
|
pause();
|
||||||
} else {
|
|
||||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
|
||||||
if (device.getBluetoothClass().hasService(BluetoothClass.Service.AUDIO))
|
|
||||||
pause();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, filter);
|
}, filter);
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,10 @@ public class Receiver extends BroadcastReceiver {
|
|||||||
Log.d("Receiver", "onReceive: " + intent);
|
Log.d("Receiver", "onReceive: " + intent);
|
||||||
if (intent.getAction() == "android.intent.action.BOOT_COMPLETED") {
|
if (intent.getAction() == "android.intent.action.BOOT_COMPLETED") {
|
||||||
if (Settings.Preferences.getBoolean(context,
|
if (Settings.Preferences.getBoolean(context,
|
||||||
Settings.Preferences.KEY_RUN_ON_BOOT, false)) {
|
Settings.Preferences.KEY_RUN_ON_BOOT,
|
||||||
final boolean wakelock = Settings.Preferences.getBoolean(context,
|
false)) {
|
||||||
|
final boolean wakelock =
|
||||||
|
Settings.Preferences.getBoolean(context,
|
||||||
Settings.Preferences.KEY_WAKELOCK, false);
|
Settings.Preferences.KEY_WAKELOCK, false);
|
||||||
Main.start(context, wakelock);
|
Main.start(context, wakelock);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ author = 'Max Kellermann'
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '0.23.5'
|
version = '0.23.8'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
#release = version + '~git'
|
#release = version + '~git'
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
# This setting sets the address for the daemon to listen on. Careful attention
|
# This setting sets the address for the daemon to listen on. Careful attention
|
||||||
# should be paid if this is assigned to anything other than the default, any.
|
# should be paid if this is assigned to anything other than the default, any.
|
||||||
# This setting can deny access to control of the daemon. Not effective if
|
# This setting can deny access to control of the daemon. Not effective if
|
||||||
# systemd socket activiation is in use.
|
# systemd socket activation is in use.
|
||||||
#
|
#
|
||||||
# For network
|
# For network
|
||||||
#bind_to_address "any"
|
#bind_to_address "any"
|
||||||
@@ -185,7 +185,7 @@
|
|||||||
# cache_directory "~/.local/share/mpd/cache"
|
# cache_directory "~/.local/share/mpd/cache"
|
||||||
#}
|
#}
|
||||||
#
|
#
|
||||||
# An example of database config for a sattelite setup
|
# An example of database config for a satellite setup
|
||||||
#
|
#
|
||||||
#music_directory "nfs://fileserver.local/srv/mp3"
|
#music_directory "nfs://fileserver.local/srv/mp3"
|
||||||
#database {
|
#database {
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ Querying :program:`MPD`'s status
|
|||||||
current song in seconds, but with higher resolution.
|
current song in seconds, but with higher resolution.
|
||||||
- ``duration`` [#since_0_20]_: Duration of the current song in seconds.
|
- ``duration`` [#since_0_20]_: Duration of the current song in seconds.
|
||||||
- ``bitrate``: instantaneous bitrate in kbps
|
- ``bitrate``: instantaneous bitrate in kbps
|
||||||
- ``xfade``: ``crossfade`` in seconds
|
- ``xfade``: ``crossfade`` in seconds (see :ref:`crossfading`)
|
||||||
- ``mixrampdb``: ``mixramp`` threshold in dB
|
- ``mixrampdb``: ``mixramp`` threshold in dB
|
||||||
- ``mixrampdelay``: ``mixrampdelay`` in seconds
|
- ``mixrampdelay``: ``mixrampdelay`` in seconds
|
||||||
- ``audio``: The format emitted by the decoder plugin during
|
- ``audio``: The format emitted by the decoder plugin during
|
||||||
@@ -519,17 +519,19 @@ Playback options
|
|||||||
.. _command_crossfade:
|
.. _command_crossfade:
|
||||||
|
|
||||||
:command:`crossfade {SECONDS}`
|
:command:`crossfade {SECONDS}`
|
||||||
Sets crossfading between songs.
|
Sets crossfading between songs. See :ref:`crossfading`.
|
||||||
|
|
||||||
.. _command_mixrampdb:
|
.. _command_mixrampdb:
|
||||||
|
|
||||||
:command:`mixrampdb {deciBels}`
|
:command:`mixrampdb {deciBels}`
|
||||||
Sets the threshold at which songs will be overlapped. Like crossfading but doesn't fade the track volume, just overlaps. The songs need to have MixRamp tags added by an external tool. 0dB is the normalized maximum volume so use negative values, I prefer -17dB. In the absence of mixramp tags crossfading will be used. See http://sourceforge.net/projects/mixramp
|
Sets the threshold at which songs will be overlapped.
|
||||||
|
See :ref:`mixramp`.
|
||||||
|
|
||||||
.. _command_mixrampdelay:
|
.. _command_mixrampdelay:
|
||||||
|
|
||||||
:command:`mixrampdelay {SECONDS}`
|
:command:`mixrampdelay {SECONDS}`
|
||||||
Additional time subtracted from the overlap calculated by mixrampdb. A value of "nan" disables MixRamp overlapping and falls back to crossfading.
|
Additional time subtracted from the overlap calculated by mixrampdb. A value of "nan" disables MixRamp overlapping and falls back to crossfading.
|
||||||
|
See :ref:`mixramp`.
|
||||||
|
|
||||||
.. _command_random:
|
.. _command_random:
|
||||||
|
|
||||||
@@ -543,6 +545,13 @@ Playback options
|
|||||||
Sets repeat state to ``STATE``,
|
Sets repeat state to ``STATE``,
|
||||||
``STATE`` should be 0 or 1.
|
``STATE`` should be 0 or 1.
|
||||||
|
|
||||||
|
If enabled, MPD keeps repeating the whole queue (:ref:`single mode
|
||||||
|
<command_single>` disabled) or the current song (:ref:`single mode
|
||||||
|
<command_single>` enabled).
|
||||||
|
|
||||||
|
If :ref:`random mode <command_random>` is also enabled, the
|
||||||
|
playback order will be shuffled each time the queue gets repeated.
|
||||||
|
|
||||||
.. _command_setvol:
|
.. _command_setvol:
|
||||||
|
|
||||||
:command:`setvol {VOL}`
|
:command:`setvol {VOL}`
|
||||||
@@ -771,8 +780,8 @@ Whenever possible, ids should be used.
|
|||||||
.. _command_playlistfind:
|
.. _command_playlistfind:
|
||||||
|
|
||||||
:command:`playlistfind {FILTER}`
|
:command:`playlistfind {FILTER}`
|
||||||
Finds songs in the queue with strict
|
Search the queue for songs matching
|
||||||
matching.
|
``FILTER`` (see :ref:`Filters <filter_syntax>`).
|
||||||
|
|
||||||
.. _command_playlistid:
|
.. _command_playlistid:
|
||||||
|
|
||||||
@@ -792,8 +801,10 @@ Whenever possible, ids should be used.
|
|||||||
.. _command_playlistsearch:
|
.. _command_playlistsearch:
|
||||||
|
|
||||||
:command:`playlistsearch {FILTER}`
|
:command:`playlistsearch {FILTER}`
|
||||||
Searches case-insensitively for partial matches in the
|
Search the queue for songs matching
|
||||||
queue.
|
``FILTER`` (see :ref:`Filters <filter_syntax>`).
|
||||||
|
Parameters have the same meaning as for :ref:`find
|
||||||
|
<command_playlistfind>`, except that search is not case sensitive.
|
||||||
|
|
||||||
.. _command_plchanges:
|
.. _command_plchanges:
|
||||||
|
|
||||||
|
|||||||
72
doc/user.rst
72
doc/user.rst
@@ -301,7 +301,7 @@ Configuring neighbor plugins
|
|||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
All neighbor plugins are disabled by default to avoid unwanted
|
All neighbor plugins are disabled by default to avoid unwanted
|
||||||
overhead. To enable (and configure) a plugin, add a :code:`neighbor`
|
overhead. To enable (and configure) a plugin, add a :code:`neighbors`
|
||||||
block to :file:`mpd.conf`:
|
block to :file:`mpd.conf`:
|
||||||
|
|
||||||
.. code-block:: none
|
.. code-block:: none
|
||||||
@@ -538,7 +538,7 @@ The following table lists the playlist_plugin options valid for all plugins:
|
|||||||
|
|
||||||
* - Name
|
* - Name
|
||||||
- Description
|
- Description
|
||||||
* - **plugin**
|
* - **name**
|
||||||
- The name of the plugin
|
- The name of the plugin
|
||||||
* - **enabled yes|no**
|
* - **enabled yes|no**
|
||||||
- Allows you to disable a playlist plugin without recompiling. By default, all plugins are enabled.
|
- Allows you to disable a playlist plugin without recompiling. By default, all plugins are enabled.
|
||||||
@@ -622,6 +622,51 @@ enabled by setting ``volume_normalization`` to ``yes``. It supports
|
|||||||
16 bit PCM only.
|
16 bit PCM only.
|
||||||
|
|
||||||
|
|
||||||
|
.. _crossfading:
|
||||||
|
|
||||||
|
Cross-Fading
|
||||||
|
------------
|
||||||
|
|
||||||
|
If ``crossfade`` is set to a positive number, then adjacent songs are
|
||||||
|
cross-faded by this number of seconds. This is a run-time setting
|
||||||
|
:ref:`which can be controlled by clients <command_crossfade>`,
|
||||||
|
e.g. with :program:`mpc`::
|
||||||
|
|
||||||
|
mpc crossfade 10
|
||||||
|
mpc crossfade 0
|
||||||
|
|
||||||
|
Zero means cross-fading is disabled.
|
||||||
|
|
||||||
|
Cross-fading is only possible if both songs have the same audio
|
||||||
|
format. At the cost of quality loss and higher CPU usage, you can
|
||||||
|
make sure this is always given by configuring
|
||||||
|
:ref:`audio_output_format`.
|
||||||
|
|
||||||
|
.. _mixramp:
|
||||||
|
|
||||||
|
MixRamp
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
MixRamp tags describe the loudness levels at start and end of a song
|
||||||
|
and can be used by MPD to find the best time to begin cross-fading.
|
||||||
|
MPD enables MixRamp if:
|
||||||
|
|
||||||
|
- Cross-fade is enabled
|
||||||
|
- :ref:`mixrampdelay <command_mixrampdelay>` is set to a positive
|
||||||
|
value, e.g.::
|
||||||
|
mpc mixrampdelay 1
|
||||||
|
- :ref:`mixrampdb <command_mixrampdb>` is set to a reasonable value,
|
||||||
|
e.g.::
|
||||||
|
mpc mixrampdb -17
|
||||||
|
- both songs have MixRamp tags
|
||||||
|
- both songs have the same audio format (or :ref:`audio_output_format`
|
||||||
|
is configured)
|
||||||
|
|
||||||
|
The `MixRamp <http://sourceforge.net/projects/mixramp>`__ tool can be
|
||||||
|
used to add MixRamp tags to your song files.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Client Connections
|
Client Connections
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@@ -1018,7 +1063,19 @@ The "music directory" is where you store your music files. :program:`MPD` stores
|
|||||||
|
|
||||||
Depending on the size of your music collection and the speed of the storage, this can take a while.
|
Depending on the size of your music collection and the speed of the storage, this can take a while.
|
||||||
|
|
||||||
To exclude a file from the update, create a file called :file:`.mpdignore` in its parent directory. Each line of that file may contain a list of shell wildcards. Matching files in the current directory and all subdirectories are excluded.
|
To exclude a file from the update, create a file called
|
||||||
|
:file:`.mpdignore` in its parent directory. Each line of that file
|
||||||
|
may contain a list of shell wildcards. Matching files (or
|
||||||
|
directories) in the current directory and all subdirectories are
|
||||||
|
excluded. Example::
|
||||||
|
|
||||||
|
*.opus
|
||||||
|
99*
|
||||||
|
|
||||||
|
Subject to pattern matching is the file/directory name. It is (not
|
||||||
|
yet) possible to match nested path names, e.g. something like
|
||||||
|
``foo/*.flac`` is not possible.
|
||||||
|
|
||||||
|
|
||||||
Mounting other storages into the music directory
|
Mounting other storages into the music directory
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@@ -1058,6 +1115,15 @@ See :ref:`tags` for a list of supported tags.
|
|||||||
The :ref:`metadata_to_use <metadata_to_use>` setting can be used to
|
The :ref:`metadata_to_use <metadata_to_use>` setting can be used to
|
||||||
enable or disable certain tags.
|
enable or disable certain tags.
|
||||||
|
|
||||||
|
Note that :program:`MPD` may not necessarily read metadata itself,
|
||||||
|
instead relying on data reported by the decoder that was used to read
|
||||||
|
a file. For example, this is the case for the FFmpeg decoder: both
|
||||||
|
:program:`MPD` and FFmpeg need to support a given metadata format in
|
||||||
|
order for metadata to be picked up correctly.
|
||||||
|
|
||||||
|
Only if a decoder does not have metadata support will :program:`MPD`
|
||||||
|
attempt to parse a song's metadata itself.
|
||||||
|
|
||||||
The queue
|
The queue
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
project(
|
project(
|
||||||
'mpd',
|
'mpd',
|
||||||
['c', 'cpp'],
|
['c', 'cpp'],
|
||||||
version: '0.23.5',
|
version: '0.23.8',
|
||||||
meson_version: '>= 0.56.0',
|
meson_version: '>= 0.56.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=c11',
|
'c_std=c11',
|
||||||
@@ -73,6 +73,9 @@ test_common_flags = [
|
|||||||
# clang specific warning options:
|
# clang specific warning options:
|
||||||
'-Wunreachable-code-aggressive',
|
'-Wunreachable-code-aggressive',
|
||||||
'-Wused-but-marked-unused',
|
'-Wused-but-marked-unused',
|
||||||
|
|
||||||
|
# suppress bogus GCC12 warnings in libfmt headers
|
||||||
|
'-Wno-stringop-overflow',
|
||||||
]
|
]
|
||||||
|
|
||||||
test_global_cxxflags = test_global_common_flags + [
|
test_global_cxxflags = test_global_common_flags + [
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ from build.boost import BoostProject
|
|||||||
from build.jack import JackProject
|
from build.jack import JackProject
|
||||||
|
|
||||||
libmpdclient = MesonProject(
|
libmpdclient = MesonProject(
|
||||||
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.19.tar.xz',
|
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.20.tar.xz',
|
||||||
'158aad4c2278ab08e76a3f2b0166c99b39fae00ee17231bd225c5a36e977a189',
|
'18793f68e939c3301e34d8fcadea1f7daa24143941263cecadb80126194e277d',
|
||||||
'lib/libmpdclient.a',
|
'lib/libmpdclient.a',
|
||||||
)
|
)
|
||||||
|
|
||||||
libogg = CmakeProject(
|
libogg = CmakeProject(
|
||||||
'http://downloads.xiph.org/releases/ogg/libogg-1.3.4.tar.xz',
|
'http://downloads.xiph.org/releases/ogg/libogg-1.3.5.tar.xz',
|
||||||
'c163bc12bc300c401b6aa35907ac682671ea376f13ae0969a220f7ddf71893fe',
|
'c4d91be36fc8e54deae7575241e03f4211eb102afb3fc0775fbbc1b740016705',
|
||||||
'lib/libogg.a',
|
'lib/libogg.a',
|
||||||
[
|
[
|
||||||
'-DBUILD_SHARED_LIBS=OFF',
|
'-DBUILD_SHARED_LIBS=OFF',
|
||||||
@@ -43,8 +43,8 @@ opus = AutotoolsProject(
|
|||||||
)
|
)
|
||||||
|
|
||||||
flac = AutotoolsProject(
|
flac = AutotoolsProject(
|
||||||
'http://downloads.xiph.org/releases/flac/flac-1.3.3.tar.xz',
|
'http://downloads.xiph.org/releases/flac/flac-1.3.4.tar.xz',
|
||||||
'213e82bd716c9de6db2f98bcadbc4c24c7e2efe8c75939a1a84e28539c4e1748',
|
'8ff0607e75a322dd7cd6ec48f4f225471404ae2730d0ea945127b1355155e737',
|
||||||
'lib/libFLAC.a',
|
'lib/libFLAC.a',
|
||||||
[
|
[
|
||||||
'--disable-shared', '--enable-static',
|
'--disable-shared', '--enable-static',
|
||||||
@@ -55,8 +55,8 @@ flac = AutotoolsProject(
|
|||||||
)
|
)
|
||||||
|
|
||||||
zlib = ZlibProject(
|
zlib = ZlibProject(
|
||||||
'http://zlib.net/zlib-1.2.11.tar.xz',
|
'http://zlib.net/zlib-1.2.12.tar.xz',
|
||||||
'4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066',
|
'7db46b8d7726232a621befaab4a1c870f00a90805511c0e0090441dac57def18',
|
||||||
'lib/libz.a',
|
'lib/libz.a',
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -151,8 +151,8 @@ gme = CmakeProject(
|
|||||||
)
|
)
|
||||||
|
|
||||||
ffmpeg = FfmpegProject(
|
ffmpeg = FfmpegProject(
|
||||||
'http://ffmpeg.org/releases/ffmpeg-4.4.1.tar.xz',
|
'http://ffmpeg.org/releases/ffmpeg-5.0.1.tar.xz',
|
||||||
'eadbad9e9ab30b25f5520fbfde99fae4a92a1ae3c0257a8d68569a4651e30e02',
|
'ef2efae259ce80a240de48ec85ecb062cecca26e4352ffb3fda562c21a93007b',
|
||||||
'lib/libavcodec.a',
|
'lib/libavcodec.a',
|
||||||
[
|
[
|
||||||
'--disable-shared', '--enable-static',
|
'--disable-shared', '--enable-static',
|
||||||
@@ -177,6 +177,8 @@ ffmpeg = FfmpegProject(
|
|||||||
'--disable-filters',
|
'--disable-filters',
|
||||||
'--disable-v4l2_m2m',
|
'--disable-v4l2_m2m',
|
||||||
|
|
||||||
|
'--disable-vulkan',
|
||||||
|
|
||||||
'--disable-parser=bmp',
|
'--disable-parser=bmp',
|
||||||
'--disable-parser=cavsvideo',
|
'--disable-parser=cavsvideo',
|
||||||
'--disable-parser=dvbsub',
|
'--disable-parser=dvbsub',
|
||||||
@@ -380,14 +382,14 @@ ffmpeg = FfmpegProject(
|
|||||||
)
|
)
|
||||||
|
|
||||||
openssl = OpenSSLProject(
|
openssl = OpenSSLProject(
|
||||||
'https://www.openssl.org/source/openssl-3.0.0.tar.gz',
|
'https://www.openssl.org/source/openssl-3.0.5.tar.gz',
|
||||||
'59eedfcb46c25214c9bd37ed6078297b4df01d012267fe9e9eee31f61bc70536',
|
'aa7d8d9bef71ad6525c55ba11e5f4397889ce49c2c9349dcea6d3e4f0b024a7a',
|
||||||
'include/openssl/ossl_typ.h',
|
'include/openssl/ossl_typ.h',
|
||||||
)
|
)
|
||||||
|
|
||||||
curl = CmakeProject(
|
curl = CmakeProject(
|
||||||
'https://curl.se/download/curl-7.79.1.tar.xz',
|
'https://curl.se/download/curl-7.84.0.tar.xz',
|
||||||
'0606f74b1182ab732a17c11613cbbaf7084f2e6cca432642d0e3ad7c224c3689',
|
'2d118b43f547bfe5bae806d8d47b4e596ea5b25a6c1f080aef49fbcd817c5db8',
|
||||||
'lib/libcurl.a',
|
'lib/libcurl.a',
|
||||||
[
|
[
|
||||||
'-DBUILD_CURL_EXE=OFF',
|
'-DBUILD_CURL_EXE=OFF',
|
||||||
@@ -415,14 +417,14 @@ curl = CmakeProject(
|
|||||||
'-DBUILD_TESTING=OFF',
|
'-DBUILD_TESTING=OFF',
|
||||||
],
|
],
|
||||||
windows_configure_args=[
|
windows_configure_args=[
|
||||||
'-DCMAKE_USE_SCHANNEL=ON',
|
'-DCURL_USE_SCHANNEL=ON',
|
||||||
],
|
],
|
||||||
patches='src/lib/curl/patches',
|
patches='src/lib/curl/patches',
|
||||||
)
|
)
|
||||||
|
|
||||||
libnfs = AutotoolsProject(
|
libnfs = AutotoolsProject(
|
||||||
'https://github.com/sahlberg/libnfs/archive/libnfs-4.0.0.tar.gz',
|
'https://github.com/sahlberg/libnfs/archive/libnfs-5.0.1.tar.gz',
|
||||||
'6ee77e9fe220e2d3e3b1f53cfea04fb319828cc7dbb97dd9df09e46e901d797d',
|
'7ef445410b42f36b9bad426608b53ccb9ccca4101e545c383f564c11db672ca8',
|
||||||
'lib/libnfs.a',
|
'lib/libnfs.a',
|
||||||
[
|
[
|
||||||
'--disable-shared', '--enable-static',
|
'--disable-shared', '--enable-static',
|
||||||
@@ -433,8 +435,7 @@ libnfs = AutotoolsProject(
|
|||||||
|
|
||||||
'--disable-utils', '--disable-examples',
|
'--disable-utils', '--disable-examples',
|
||||||
],
|
],
|
||||||
base='libnfs-libnfs-4.0.0',
|
base='libnfs-libnfs-5.0.1',
|
||||||
patches='src/lib/nfs/patches',
|
|
||||||
autoreconf=True,
|
autoreconf=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -445,7 +446,7 @@ jack = JackProject(
|
|||||||
)
|
)
|
||||||
|
|
||||||
boost = BoostProject(
|
boost = BoostProject(
|
||||||
'https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.bz2',
|
'https://boostorg.jfrog.io/artifactory/main/release/1.79.0/source/boost_1_79_0.tar.bz2',
|
||||||
'fc9f85fc030e233142908241af7a846e60630aa7388de9a5fafb1f3a26840854',
|
'475d589d51a7f8b3ba2ba4eda022b170e562ca3b760ee922c146b6c65856ef39',
|
||||||
'include/boost/version.hpp',
|
'include/boost/version.hpp',
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -53,19 +53,21 @@ pkgconfig = '{toolchain.pkg_config}'
|
|||||||
f.write(f"""
|
f.write(f"""
|
||||||
[properties]
|
[properties]
|
||||||
root = '{toolchain.install_prefix}'
|
root = '{toolchain.install_prefix}'
|
||||||
|
|
||||||
[built-in options]
|
|
||||||
c_args = {repr((toolchain.cppflags + ' ' + toolchain.cflags).split())}
|
|
||||||
c_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
|
|
||||||
|
|
||||||
cpp_args = {repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split())}
|
|
||||||
cpp_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
|
|
||||||
""")
|
""")
|
||||||
|
|
||||||
if 'android' in toolchain.arch:
|
if 'android' in toolchain.arch:
|
||||||
f.write("""
|
f.write("""
|
||||||
# Keep Meson from executing Android-x86 test binariees
|
# Keep Meson from executing Android-x86 test binariees
|
||||||
needs_exe_wrapper = true
|
needs_exe_wrapper = true
|
||||||
|
""")
|
||||||
|
|
||||||
|
f.write(f"""
|
||||||
|
[built-in options]
|
||||||
|
c_args = {repr((toolchain.cppflags + ' ' + toolchain.cflags).split())}
|
||||||
|
c_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
|
||||||
|
|
||||||
|
cpp_args = {repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split())}
|
||||||
|
cpp_link_args = {repr(toolchain.ldflags.split() + toolchain.libs.split())}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
f.write(f"""
|
f.write(f"""
|
||||||
|
|||||||
@@ -48,15 +48,14 @@ LocateFileUri(const char *uri, const Client *client
|
|||||||
/* this path was relative to the music
|
/* this path was relative to the music
|
||||||
directory */
|
directory */
|
||||||
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
|
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
|
||||||
return LocatedUri(LocatedUri::Type::RELATIVE,
|
return {LocatedUri::Type::RELATIVE, suffix.data()};
|
||||||
suffix.data());
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (client != nullptr)
|
if (client != nullptr)
|
||||||
client->AllowFile(path);
|
client->AllowFile(path);
|
||||||
|
|
||||||
return LocatedUri(LocatedUri::Type::PATH, uri, std::move(path));
|
return {LocatedUri::Type::PATH, uri, std::move(path)};
|
||||||
}
|
}
|
||||||
|
|
||||||
static LocatedUri
|
static LocatedUri
|
||||||
@@ -90,8 +89,7 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
|
|||||||
const auto suffix = storage->MapToRelativeUTF8(uri);
|
const auto suffix = storage->MapToRelativeUTF8(uri);
|
||||||
if (suffix.data() != nullptr)
|
if (suffix.data() != nullptr)
|
||||||
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
|
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
|
||||||
return LocatedUri(LocatedUri::Type::RELATIVE,
|
return {LocatedUri::Type::RELATIVE, suffix.data()};
|
||||||
suffix.data());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kind == UriPluginKind::STORAGE &&
|
if (kind == UriPluginKind::STORAGE &&
|
||||||
@@ -99,7 +97,7 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
|
|||||||
throw std::invalid_argument("Unsupported URI scheme");
|
throw std::invalid_argument("Unsupported URI scheme");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return LocatedUri(LocatedUri::Type::ABSOLUTE, uri);
|
return {LocatedUri::Type::ABSOLUTE, uri};
|
||||||
}
|
}
|
||||||
|
|
||||||
LocatedUri
|
LocatedUri
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ void
|
|||||||
LogFmt(LogLevel level, const Domain &domain,
|
LogFmt(LogLevel level, const Domain &domain,
|
||||||
const S &format_str, Args&&... args) noexcept
|
const S &format_str, Args&&... args) noexcept
|
||||||
{
|
{
|
||||||
#if FMT_VERSION >= 70000
|
#if FMT_VERSION >= 90000
|
||||||
|
return LogVFmt(level, domain, format_str,
|
||||||
|
fmt::make_format_args(args...));
|
||||||
|
#elif FMT_VERSION >= 70000
|
||||||
return LogVFmt(level, domain, fmt::to_string_view(format_str),
|
return LogVFmt(level, domain, fmt::to_string_view(format_str),
|
||||||
fmt::make_args_checked<Args...>(format_str,
|
fmt::make_args_checked<Args...>(format_str,
|
||||||
args...));
|
args...));
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
|
|
||||||
#include "PlaylistDatabase.hxx"
|
#include "PlaylistDatabase.hxx"
|
||||||
#include "db/PlaylistVector.hxx"
|
#include "db/PlaylistVector.hxx"
|
||||||
#include "fs/io/TextFile.hxx"
|
#include "io/LineReader.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "io/BufferedOutputStream.hxx"
|
||||||
#include "time/ChronoUtil.hxx"
|
#include "time/ChronoUtil.hxx"
|
||||||
#include "util/StringStrip.hxx"
|
#include "util/StringStrip.hxx"
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
@@ -42,7 +42,7 @@ playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name)
|
playlist_metadata_load(LineReader &file, PlaylistVector &pv, const char *name)
|
||||||
{
|
{
|
||||||
PlaylistInfo pm(name);
|
PlaylistInfo pm(name);
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
class PlaylistVector;
|
class PlaylistVector;
|
||||||
class BufferedOutputStream;
|
class BufferedOutputStream;
|
||||||
class TextFile;
|
class LineReader;
|
||||||
|
|
||||||
void
|
void
|
||||||
playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv);
|
playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv);
|
||||||
@@ -33,6 +33,7 @@ playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv);
|
|||||||
* Throws #std::runtime_error on error.
|
* Throws #std::runtime_error on error.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name);
|
playlist_metadata_load(LineReader &file, PlaylistVector &pv,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
#include "Mapper.hxx"
|
#include "Mapper.hxx"
|
||||||
#include "protocol/RangeArg.hxx"
|
#include "protocol/RangeArg.hxx"
|
||||||
#include "fs/io/TextFile.hxx"
|
#include "fs/io/TextFile.hxx"
|
||||||
#include "fs/io/FileOutputStream.hxx"
|
#include "io/FileOutputStream.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "io/BufferedOutputStream.hxx"
|
||||||
#include "config/Data.hxx"
|
#include "config/Data.hxx"
|
||||||
#include "config/Option.hxx"
|
#include "config/Option.hxx"
|
||||||
#include "config/Defaults.hxx"
|
#include "config/Defaults.hxx"
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileSystem.hxx"
|
||||||
#include "fs/io/FileOutputStream.hxx"
|
#include "io/FileOutputStream.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "io/BufferedOutputStream.hxx"
|
||||||
#include "util/UriExtract.hxx"
|
#include "util/UriExtract.hxx"
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ enum class SingleMode : uint8_t {
|
|||||||
/**
|
/**
|
||||||
* Return the string representation of a #SingleMode.
|
* Return the string representation of a #SingleMode.
|
||||||
*/
|
*/
|
||||||
[[gnu::pure]]
|
[[gnu::const]]
|
||||||
const char *
|
const char *
|
||||||
SingleToString(SingleMode mode) noexcept;
|
SingleToString(SingleMode mode) noexcept;
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@
|
|||||||
#include "db/plugins/simple/Song.hxx"
|
#include "db/plugins/simple/Song.hxx"
|
||||||
#include "song/DetachedSong.hxx"
|
#include "song/DetachedSong.hxx"
|
||||||
#include "TagSave.hxx"
|
#include "TagSave.hxx"
|
||||||
#include "fs/io/TextFile.hxx"
|
#include "io/LineReader.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "io/BufferedOutputStream.hxx"
|
||||||
#include "tag/ParseName.hxx"
|
#include "tag/ParseName.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "tag/Builder.hxx"
|
#include "tag/Builder.hxx"
|
||||||
@@ -85,7 +85,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song)
|
|||||||
}
|
}
|
||||||
|
|
||||||
DetachedSong
|
DetachedSong
|
||||||
song_load(TextFile &file, const char *uri,
|
song_load(LineReader &file, const char *uri,
|
||||||
std::string *target_r)
|
std::string *target_r)
|
||||||
{
|
{
|
||||||
DetachedSong song(uri);
|
DetachedSong song(uri);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ struct Song;
|
|||||||
struct AudioFormat;
|
struct AudioFormat;
|
||||||
class DetachedSong;
|
class DetachedSong;
|
||||||
class BufferedOutputStream;
|
class BufferedOutputStream;
|
||||||
class TextFile;
|
class LineReader;
|
||||||
|
|
||||||
void
|
void
|
||||||
song_save(BufferedOutputStream &os, const Song &song);
|
song_save(BufferedOutputStream &os, const Song &song);
|
||||||
@@ -43,7 +43,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song);
|
|||||||
* Throws on error.
|
* Throws on error.
|
||||||
*/
|
*/
|
||||||
DetachedSong
|
DetachedSong
|
||||||
song_load(TextFile &file, const char *uri,
|
song_load(LineReader &file, const char *uri,
|
||||||
std::string *target_r=nullptr);
|
std::string *target_r=nullptr);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -22,8 +22,8 @@
|
|||||||
#include "output/State.hxx"
|
#include "output/State.hxx"
|
||||||
#include "queue/PlaylistState.hxx"
|
#include "queue/PlaylistState.hxx"
|
||||||
#include "fs/io/TextFile.hxx"
|
#include "fs/io/TextFile.hxx"
|
||||||
#include "fs/io/FileOutputStream.hxx"
|
#include "io/FileOutputStream.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "io/BufferedOutputStream.hxx"
|
||||||
#include "storage/StorageState.hxx"
|
#include "storage/StorageState.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
#include "Instance.hxx"
|
#include "Instance.hxx"
|
||||||
|
|||||||
@@ -21,12 +21,16 @@
|
|||||||
#include "TagStream.hxx"
|
#include "TagStream.hxx"
|
||||||
#include "TagFile.hxx"
|
#include "TagFile.hxx"
|
||||||
#include "tag/Generic.hxx"
|
#include "tag/Generic.hxx"
|
||||||
|
#include "song/LightSong.hxx"
|
||||||
|
#include "db/Interface.hxx"
|
||||||
#include "storage/StorageInterface.hxx"
|
#include "storage/StorageInterface.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
#include "protocol/Ack.hxx"
|
#include "protocol/Ack.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "input/InputStream.hxx"
|
#include "input/InputStream.hxx"
|
||||||
#include "util/Compiler.h"
|
#include "util/Compiler.h"
|
||||||
|
#include "util/ScopeExit.hxx"
|
||||||
|
#include "util/StringCompare.hxx"
|
||||||
#include "util/UriExtract.hxx"
|
#include "util/UriExtract.hxx"
|
||||||
#include "LocateUri.hxx"
|
#include "LocateUri.hxx"
|
||||||
|
|
||||||
@@ -51,10 +55,67 @@ TagScanFile(const Path path_fs, TagHandler &handler)
|
|||||||
ScanGenericTags(path_fs, handler);
|
ScanGenericTags(path_fs, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_DATABASE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse "../" prefixes in a URI relative to the specified base
|
||||||
|
* URI.
|
||||||
|
*/
|
||||||
|
static std::string
|
||||||
|
ResolveUri(std::string_view base, const char *relative)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
const char *rest = StringAfterPrefix(relative, "../");
|
||||||
|
if (rest == nullptr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (base == ".")
|
||||||
|
throw ProtocolError(ACK_ERROR_NO_EXIST, "Bad real URI");
|
||||||
|
|
||||||
|
base = PathTraitsUTF8::GetParent(base);
|
||||||
|
relative = rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PathTraitsUTF8::Build(base, relative);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up the specified song in the database and return its
|
||||||
|
* (resolved) "real" URI.
|
||||||
|
*/
|
||||||
|
static std::string
|
||||||
|
GetRealSongUri(Client &client, std::string_view uri)
|
||||||
|
{
|
||||||
|
const auto &db = client.GetDatabaseOrThrow();
|
||||||
|
|
||||||
|
const auto *song = db.GetSong(uri);
|
||||||
|
if (song == nullptr)
|
||||||
|
throw ProtocolError(ACK_ERROR_NO_EXIST, "No such song");
|
||||||
|
|
||||||
|
AtScopeExit(&db, song) { db.ReturnSong(song); };
|
||||||
|
|
||||||
|
if (song->real_uri == nullptr)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return ResolveUri(PathTraitsUTF8::GetParent(uri), song->real_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
TagScanDatabase(Client &client, const char *uri, TagHandler &handler)
|
TagScanDatabase(Client &client, const char *uri, TagHandler &handler)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_DATABASE
|
#ifdef ENABLE_DATABASE
|
||||||
|
const auto real_uri = GetRealSongUri(client, uri);
|
||||||
|
|
||||||
|
if (!real_uri.empty()) {
|
||||||
|
uri = real_uri.c_str();
|
||||||
|
|
||||||
|
// TODO: support absolute paths?
|
||||||
|
if (uri_has_scheme(uri))
|
||||||
|
return TagScanStream(uri, handler);
|
||||||
|
}
|
||||||
|
|
||||||
const Storage *storage = client.GetStorage();
|
const Storage *storage = client.GetStorage();
|
||||||
if (storage == nullptr) {
|
if (storage == nullptr) {
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include "TagSave.hxx"
|
#include "TagSave.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "io/BufferedOutputStream.hxx"
|
||||||
|
|
||||||
#define SONG_TIME "Time: "
|
#define SONG_TIME "Time: "
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "ErrorRef.hxx"
|
#include "ErrorRef.hxx"
|
||||||
#include "StringRef.hxx"
|
#include "StringRef.hxx"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace Apple {
|
namespace Apple {
|
||||||
@@ -57,8 +58,8 @@ ThrowOSStatus(OSStatus status, const char *_msg)
|
|||||||
const Apple::StringRef cfstr(cferr.CopyDescription());
|
const Apple::StringRef cfstr(cferr.CopyDescription());
|
||||||
|
|
||||||
char msg[1024];
|
char msg[1024];
|
||||||
strcpy(msg, _msg);
|
std::strcpy(msg, _msg);
|
||||||
size_t length = strlen(msg);
|
size_t length = std::strlen(msg);
|
||||||
|
|
||||||
cfstr.GetCString(msg + length, sizeof(msg) - length);
|
cfstr.GetCString(msg + length, sizeof(msg) - length);
|
||||||
throw std::runtime_error(msg);
|
throw std::runtime_error(msg);
|
||||||
|
|||||||
@@ -82,7 +82,10 @@ public:
|
|||||||
|
|
||||||
template<typename S, typename... Args>
|
template<typename S, typename... Args>
|
||||||
bool Fmt(const S &format_str, Args&&... args) noexcept {
|
bool Fmt(const S &format_str, Args&&... args) noexcept {
|
||||||
#if FMT_VERSION >= 70000
|
#if FMT_VERSION >= 90000
|
||||||
|
return VFmt(format_str,
|
||||||
|
fmt::make_format_args(args...));
|
||||||
|
#elif FMT_VERSION >= 70000
|
||||||
return VFmt(fmt::to_string_view(format_str),
|
return VFmt(fmt::to_string_view(format_str),
|
||||||
fmt::make_args_checked<Args...>(format_str,
|
fmt::make_args_checked<Args...>(format_str,
|
||||||
args...));
|
args...));
|
||||||
@@ -109,7 +112,10 @@ public:
|
|||||||
template<typename S, typename... Args>
|
template<typename S, typename... Args>
|
||||||
void FmtError(enum ack code,
|
void FmtError(enum ack code,
|
||||||
const S &format_str, Args&&... args) noexcept {
|
const S &format_str, Args&&... args) noexcept {
|
||||||
#if FMT_VERSION >= 70000
|
#if FMT_VERSION >= 90000
|
||||||
|
return VFmtError(code, format_str,
|
||||||
|
fmt::make_format_args(args...));
|
||||||
|
#elif FMT_VERSION >= 70000
|
||||||
return VFmtError(code, fmt::to_string_view(format_str),
|
return VFmtError(code, fmt::to_string_view(format_str),
|
||||||
fmt::make_args_checked<Args...>(format_str,
|
fmt::make_args_checked<Args...>(format_str,
|
||||||
args...));
|
args...));
|
||||||
|
|||||||
@@ -160,8 +160,7 @@ find_stream_art(std::string_view directory, Mutex &mutex)
|
|||||||
static constexpr auto art_names = std::array {
|
static constexpr auto art_names = std::array {
|
||||||
"cover.png",
|
"cover.png",
|
||||||
"cover.jpg",
|
"cover.jpg",
|
||||||
"cover.tiff",
|
"cover.webp",
|
||||||
"cover.bmp",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for(const auto name : art_names) {
|
for(const auto name : art_names) {
|
||||||
|
|||||||
@@ -333,15 +333,11 @@ handle_getvol(Client &client, Request, Response &r)
|
|||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_setvol(Client &client, Request args, Response &r)
|
handle_setvol(Client &client, Request args, Response &)
|
||||||
{
|
{
|
||||||
unsigned level = args.ParseUnsigned(0, 100);
|
unsigned level = args.ParseUnsigned(0, 100);
|
||||||
|
|
||||||
if (!volume_level_change(client.GetPartition().outputs, level)) {
|
volume_level_change(client.GetPartition().outputs, level);
|
||||||
r.Error(ACK_ERROR_SYSTEM, "problems setting volume");
|
|
||||||
return CommandResult::ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,11 +360,8 @@ handle_volume(Client &client, Request args, Response &r)
|
|||||||
else if (new_volume > 100)
|
else if (new_volume > 100)
|
||||||
new_volume = 100;
|
new_volume = 100;
|
||||||
|
|
||||||
if (new_volume != old_volume &&
|
if (new_volume != old_volume)
|
||||||
!volume_level_change(outputs, new_volume)) {
|
volume_level_change(outputs, new_volume);
|
||||||
r.Error(ACK_ERROR_SYSTEM, "problems setting volume");
|
|
||||||
return CommandResult::ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@
|
|||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileSystem.hxx"
|
||||||
#include "fs/List.hxx"
|
#include "fs/List.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
#include "fs/io/FileReader.hxx"
|
#include "io/FileReader.hxx"
|
||||||
#include "fs/io/BufferedReader.hxx"
|
#include "io/BufferedReader.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
#include "DatabaseSave.hxx"
|
#include "DatabaseSave.hxx"
|
||||||
#include "db/DatabaseLock.hxx"
|
#include "db/DatabaseLock.hxx"
|
||||||
#include "DirectorySave.hxx"
|
#include "DirectorySave.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "io/BufferedOutputStream.hxx"
|
||||||
#include "fs/io/TextFile.hxx"
|
#include "io/LineReader.hxx"
|
||||||
#include "tag/ParseName.hxx"
|
#include "tag/ParseName.hxx"
|
||||||
#include "tag/Settings.hxx"
|
#include "tag/Settings.hxx"
|
||||||
#include "fs/Charset.hxx"
|
#include "fs/Charset.hxx"
|
||||||
@@ -64,7 +64,7 @@ db_save_internal(BufferedOutputStream &os, const Directory &music_root)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
db_load_internal(TextFile &file, Directory &music_root)
|
db_load_internal(LineReader &file, Directory &music_root)
|
||||||
{
|
{
|
||||||
char *line;
|
char *line;
|
||||||
unsigned format = 0;
|
unsigned format = 0;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
struct Directory;
|
struct Directory;
|
||||||
class BufferedOutputStream;
|
class BufferedOutputStream;
|
||||||
class TextFile;
|
class LineReader;
|
||||||
|
|
||||||
void
|
void
|
||||||
db_save_internal(BufferedOutputStream &os, const Directory &root);
|
db_save_internal(BufferedOutputStream &os, const Directory &root);
|
||||||
@@ -31,6 +31,6 @@ db_save_internal(BufferedOutputStream &os, const Directory &root);
|
|||||||
* Throws #std::runtime_error on error.
|
* Throws #std::runtime_error on error.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
db_load_internal(TextFile &file, Directory &root);
|
db_load_internal(LineReader &file, Directory &root);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
#include "SongSave.hxx"
|
#include "SongSave.hxx"
|
||||||
#include "song/DetachedSong.hxx"
|
#include "song/DetachedSong.hxx"
|
||||||
#include "PlaylistDatabase.hxx"
|
#include "PlaylistDatabase.hxx"
|
||||||
#include "fs/io/TextFile.hxx"
|
#include "io/LineReader.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "io/BufferedOutputStream.hxx"
|
||||||
#include "time/ChronoUtil.hxx"
|
#include "time/ChronoUtil.hxx"
|
||||||
#include "util/StringAPI.hxx"
|
#include "util/StringAPI.hxx"
|
||||||
#include "util/StringCompare.hxx"
|
#include "util/StringCompare.hxx"
|
||||||
@@ -121,7 +121,7 @@ ParseLine(Directory &directory, const char *line)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Directory *
|
static Directory *
|
||||||
directory_load_subdir(TextFile &file, Directory &parent, std::string_view name)
|
directory_load_subdir(LineReader &file, Directory &parent, std::string_view name)
|
||||||
{
|
{
|
||||||
if (parent.FindChild(name) != nullptr)
|
if (parent.FindChild(name) != nullptr)
|
||||||
throw FormatRuntimeError("Duplicate subdirectory '%.*s'",
|
throw FormatRuntimeError("Duplicate subdirectory '%.*s'",
|
||||||
@@ -152,7 +152,7 @@ directory_load_subdir(TextFile &file, Directory &parent, std::string_view name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
directory_load(TextFile &file, Directory &directory)
|
directory_load(LineReader &file, Directory &directory)
|
||||||
{
|
{
|
||||||
const char *line;
|
const char *line;
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#define MPD_DIRECTORY_SAVE_HXX
|
#define MPD_DIRECTORY_SAVE_HXX
|
||||||
|
|
||||||
struct Directory;
|
struct Directory;
|
||||||
class TextFile;
|
class LineReader;
|
||||||
class BufferedOutputStream;
|
class BufferedOutputStream;
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -31,6 +31,6 @@ directory_save(BufferedOutputStream &os, const Directory &directory);
|
|||||||
* Throws #std::runtime_error on error.
|
* Throws #std::runtime_error on error.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
directory_load(TextFile &file, Directory &directory);
|
directory_load(LineReader &file, Directory &directory);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -34,8 +34,8 @@
|
|||||||
#include "db/DatabaseLock.hxx"
|
#include "db/DatabaseLock.hxx"
|
||||||
#include "db/DatabaseError.hxx"
|
#include "db/DatabaseError.hxx"
|
||||||
#include "fs/io/TextFile.hxx"
|
#include "fs/io/TextFile.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "io/BufferedOutputStream.hxx"
|
||||||
#include "fs/io/FileOutputStream.hxx"
|
#include "io/FileOutputStream.hxx"
|
||||||
#include "fs/FileInfo.hxx"
|
#include "fs/FileInfo.hxx"
|
||||||
#include "config/Block.hxx"
|
#include "config/Block.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileSystem.hxx"
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#ifdef ENABLE_ZLIB
|
#ifdef ENABLE_ZLIB
|
||||||
#include "fs/io/GzipOutputStream.hxx"
|
#include "lib/zlib/GzipOutputStream.hxx"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|||||||
@@ -257,6 +257,12 @@ public:
|
|||||||
return HasFailed();
|
return HasFailed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[gnu::pure]]
|
||||||
|
bool LockIsReplayGainEnabled() const noexcept {
|
||||||
|
const std::scoped_lock<Mutex> protect(mutex);
|
||||||
|
return replay_gain_mode != ReplayGainMode::OFF;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transition this obejct from DecoderState::START to
|
* Transition this obejct from DecoderState::START to
|
||||||
* DecoderState::DECODE.
|
* DecoderState::DECODE.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#ifndef MPD_DECODER_READER_HXX
|
#ifndef MPD_DECODER_READER_HXX
|
||||||
#define MPD_DECODER_READER_HXX
|
#define MPD_DECODER_READER_HXX
|
||||||
|
|
||||||
#include "fs/io/Reader.hxx"
|
#include "io/Reader.hxx"
|
||||||
|
|
||||||
class DecoderClient;
|
class DecoderClient;
|
||||||
class InputStream;
|
class InputStream;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
#include "util/ScopeExit.hxx"
|
#include "util/ScopeExit.hxx"
|
||||||
|
#include "util/StringCompare.hxx"
|
||||||
#include "thread/Name.hxx"
|
#include "thread/Name.hxx"
|
||||||
#include "tag/ApeReplayGain.hxx"
|
#include "tag/ApeReplayGain.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
@@ -261,12 +262,16 @@ LoadReplayGain(DecoderClient &client, InputStream &is)
|
|||||||
static void
|
static void
|
||||||
MaybeLoadReplayGain(DecoderBridge &bridge, InputStream &is)
|
MaybeLoadReplayGain(DecoderBridge &bridge, InputStream &is)
|
||||||
{
|
{
|
||||||
{
|
if (!bridge.dc.LockIsReplayGainEnabled())
|
||||||
const std::scoped_lock<Mutex> protect(bridge.dc.mutex);
|
|
||||||
if (bridge.dc.replay_gain_mode == ReplayGainMode::OFF)
|
|
||||||
/* ReplayGain is disabled */
|
/* ReplayGain is disabled */
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
if (is.HasMimeType() &&
|
||||||
|
StringStartsWith(is.GetMimeType(), "audio/x-mpd-"))
|
||||||
|
/* skip for (virtual) files (e.g. from the
|
||||||
|
cdio_paranoia input plugin) which cannot possibly
|
||||||
|
contain tags */
|
||||||
|
return;
|
||||||
|
|
||||||
LoadReplayGain(bridge, is);
|
LoadReplayGain(bridge, is);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ static void
|
|||||||
FfmpegParseMetaData(const AVStream &stream,
|
FfmpegParseMetaData(const AVStream &stream,
|
||||||
ReplayGainInfo &rg, MixRampInfo &mr)
|
ReplayGainInfo &rg, MixRampInfo &mr)
|
||||||
{
|
{
|
||||||
|
if (stream.metadata != nullptr)
|
||||||
FfmpegParseMetaData(*stream.metadata, rg, mr);
|
FfmpegParseMetaData(*stream.metadata, rg, mr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,7 +394,9 @@ FfmpegParseMetaData(const AVFormatContext &format_context, int audio_stream,
|
|||||||
{
|
{
|
||||||
assert(audio_stream >= 0);
|
assert(audio_stream >= 0);
|
||||||
|
|
||||||
|
if (format_context.metadata != nullptr)
|
||||||
FfmpegParseMetaData(*format_context.metadata, rg, mr);
|
FfmpegParseMetaData(*format_context.metadata, rg, mr);
|
||||||
|
|
||||||
FfmpegParseMetaData(*format_context.streams[audio_stream],
|
FfmpegParseMetaData(*format_context.streams[audio_stream],
|
||||||
rg, mr);
|
rg, mr);
|
||||||
}
|
}
|
||||||
@@ -468,7 +471,7 @@ static bool
|
|||||||
IsSeekable(const AVFormatContext &format_context) noexcept
|
IsSeekable(const AVFormatContext &format_context) noexcept
|
||||||
{
|
{
|
||||||
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 6, 100)
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 6, 100)
|
||||||
return (format_context.ctx_flags & AVFMTCTX_UNSEEKABLE) != 0;
|
return (format_context.ctx_flags & AVFMTCTX_UNSEEKABLE) == 0;
|
||||||
#else
|
#else
|
||||||
(void)format_context;
|
(void)format_context;
|
||||||
return false;
|
return false;
|
||||||
@@ -530,9 +533,8 @@ FfmpegDecode(DecoderClient &client, InputStream *input,
|
|||||||
: FromFfmpegTimeChecked(format_context.duration, AV_TIME_BASE_Q);
|
: FromFfmpegTimeChecked(format_context.duration, AV_TIME_BASE_Q);
|
||||||
|
|
||||||
client.Ready(audio_format,
|
client.Ready(audio_format,
|
||||||
input
|
(input ? input->IsSeekable() : false)
|
||||||
? input->IsSeekable()
|
|| IsSeekable(format_context),
|
||||||
: IsSeekable(format_context),
|
|
||||||
total_time);
|
total_time);
|
||||||
|
|
||||||
FfmpegParseMetaData(client, format_context, audio_stream);
|
FfmpegParseMetaData(client, format_context, audio_stream);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#define __STDC_CONSTANT_MACROS
|
#define __STDC_CONSTANT_MACROS
|
||||||
|
|
||||||
#include "FfmpegIo.hxx"
|
#include "FfmpegIo.hxx"
|
||||||
|
#include "libavutil/mem.h"
|
||||||
#include "../DecoderAPI.hxx"
|
#include "../DecoderAPI.hxx"
|
||||||
#include "input/InputStream.hxx"
|
#include "input/InputStream.hxx"
|
||||||
|
|
||||||
@@ -35,7 +36,11 @@ AvioStream::~AvioStream()
|
|||||||
inline int
|
inline int
|
||||||
AvioStream::Read(void *dest, int size)
|
AvioStream::Read(void *dest, int size)
|
||||||
{
|
{
|
||||||
return decoder_read(client, input, dest, size);
|
const auto nbytes = decoder_read(client, input, dest, size);
|
||||||
|
if (nbytes == 0)
|
||||||
|
return AVERROR_EOF;
|
||||||
|
|
||||||
|
return nbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int64_t
|
inline int64_t
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ ScanOpusTags(const void *data, size_t size,
|
|||||||
if (!r.Expect("OpusTags", 8))
|
if (!r.Expect("OpusTags", 8))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!handler.WantPair() && !handler.WantTag())
|
if (!handler.WantPair() && !handler.WantTag() &&
|
||||||
|
!handler.WantPicture())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!r.SkipString())
|
if (!r.SkipString())
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "lib/icu/Converter.hxx"
|
#include "lib/icu/Converter.hxx"
|
||||||
#ifdef HAVE_SIDPLAYFP
|
#ifdef HAVE_SIDPLAYFP
|
||||||
#include "fs/io/FileReader.hxx"
|
#include "io/FileReader.hxx"
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
#endif
|
#endif
|
||||||
#include "util/StringFormat.hxx"
|
#include "util/StringFormat.hxx"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include "ToOutputStream.hxx"
|
#include "ToOutputStream.hxx"
|
||||||
#include "EncoderInterface.hxx"
|
#include "EncoderInterface.hxx"
|
||||||
#include "fs/io/OutputStream.hxx"
|
#include "io/OutputStream.hxx"
|
||||||
|
|
||||||
void
|
void
|
||||||
EncoderToOutputStream(OutputStream &os, Encoder &encoder)
|
EncoderToOutputStream(OutputStream &os, Encoder &encoder)
|
||||||
|
|||||||
@@ -3,6 +3,23 @@ encoder_features = configuration_data()
|
|||||||
encoder_features.set('ENABLE_ENCODER', need_encoder)
|
encoder_features.set('ENABLE_ENCODER', need_encoder)
|
||||||
|
|
||||||
if not need_encoder
|
if not need_encoder
|
||||||
|
if need_wave_encoder
|
||||||
|
# Special case for the Snapcast output plugin which only needs the
|
||||||
|
# PCM wave encoder encoder plugin
|
||||||
|
encoder_glue = static_library(
|
||||||
|
'encoder_glue',
|
||||||
|
'plugins/WaveEncoderPlugin.cxx',
|
||||||
|
include_directories: inc,
|
||||||
|
)
|
||||||
|
|
||||||
|
encoder_glue_dep = declare_dependency(
|
||||||
|
link_with: encoder_glue,
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(output: 'Features.h', configuration: encoder_features)
|
||||||
|
subdir_done()
|
||||||
|
endif
|
||||||
|
|
||||||
encoder_glue_dep = dependency('', required: false)
|
encoder_glue_dep = dependency('', required: false)
|
||||||
configure_file(output: 'Features.h', configuration: encoder_features)
|
configure_file(output: 'Features.h', configuration: encoder_features)
|
||||||
subdir_done()
|
subdir_done()
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ if libshine_dep.found()
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
encoder_features.set('ENABLE_WAVE_ENCODER', get_option('wave_encoder'))
|
encoder_features.set('ENABLE_WAVE_ENCODER', get_option('wave_encoder'))
|
||||||
if get_option('wave_encoder')
|
if get_option('wave_encoder') or need_wave_encoder
|
||||||
encoder_plugins_sources += 'WaveEncoderPlugin.cxx'
|
encoder_plugins_sources += 'WaveEncoderPlugin.cxx'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
#include "filter/Prepared.hxx"
|
#include "filter/Prepared.hxx"
|
||||||
#include "pcm/Buffer.hxx"
|
#include "pcm/Buffer.hxx"
|
||||||
#include "pcm/AudioFormat.hxx"
|
#include "pcm/AudioFormat.hxx"
|
||||||
#include "AudioCompress/compress.h"
|
#include "pcm/AudioCompress/compress.h"
|
||||||
#include "util/ConstBuffer.hxx"
|
#include "util/ConstBuffer.hxx"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ endif
|
|||||||
|
|
||||||
filter_plugins = static_library(
|
filter_plugins = static_library(
|
||||||
'filter_plugins',
|
'filter_plugins',
|
||||||
'../../AudioCompress/compress.c',
|
|
||||||
'NullFilterPlugin.cxx',
|
'NullFilterPlugin.cxx',
|
||||||
'TwoFilters.cxx',
|
'TwoFilters.cxx',
|
||||||
'AutoConvertFilterPlugin.cxx',
|
'AutoConvertFilterPlugin.cxx',
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
#ifdef USE_XDG
|
#ifdef USE_XDG
|
||||||
#include "util/StringStrip.hxx"
|
#include "util/StringStrip.hxx"
|
||||||
#include "util/StringCompare.hxx"
|
#include "util/StringCompare.hxx"
|
||||||
#include "io/TextFile.hxx"
|
#include "fs/io/TextFile.hxx"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -18,9 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "TextFile.hxx"
|
#include "TextFile.hxx"
|
||||||
#include "FileReader.hxx"
|
#include "io/FileReader.hxx"
|
||||||
#include "AutoGunzipReader.hxx"
|
#include "io/BufferedReader.hxx"
|
||||||
#include "BufferedReader.hxx"
|
#include "lib/zlib/AutoGunzipReader.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#ifndef MPD_TEXT_FILE_HXX
|
#ifndef MPD_TEXT_FILE_HXX
|
||||||
#define MPD_TEXT_FILE_HXX
|
#define MPD_TEXT_FILE_HXX
|
||||||
|
|
||||||
|
#include "io/LineReader.hxx"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -29,7 +30,7 @@ class FileReader;
|
|||||||
class AutoGunzipReader;
|
class AutoGunzipReader;
|
||||||
class BufferedReader;
|
class BufferedReader;
|
||||||
|
|
||||||
class TextFile {
|
class TextFile final : public LineReader {
|
||||||
const std::unique_ptr<FileReader> file_reader;
|
const std::unique_ptr<FileReader> file_reader;
|
||||||
|
|
||||||
#ifdef ENABLE_ZLIB
|
#ifdef ENABLE_ZLIB
|
||||||
@@ -45,14 +46,8 @@ public:
|
|||||||
|
|
||||||
~TextFile() noexcept;
|
~TextFile() noexcept;
|
||||||
|
|
||||||
/**
|
/* virtual methods from class LineReader */
|
||||||
* Reads a line from the input file, and strips trailing
|
char *ReadLine() override;
|
||||||
* space. There is a reasonable maximum line length, only to
|
|
||||||
* prevent denial of service.
|
|
||||||
*
|
|
||||||
* @return a pointer to the line, or nullptr on end-of-file
|
|
||||||
*/
|
|
||||||
char *ReadLine();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,12 +14,7 @@ fs_sources = [
|
|||||||
'CheckFile.cxx',
|
'CheckFile.cxx',
|
||||||
'LookupFile.cxx',
|
'LookupFile.cxx',
|
||||||
'DirectoryReader.cxx',
|
'DirectoryReader.cxx',
|
||||||
'io/PeekReader.cxx',
|
|
||||||
'io/FileReader.cxx',
|
|
||||||
'io/BufferedReader.cxx',
|
|
||||||
'io/TextFile.cxx',
|
'io/TextFile.cxx',
|
||||||
'io/FileOutputStream.cxx',
|
|
||||||
'io/BufferedOutputStream.cxx',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if is_windows
|
if is_windows
|
||||||
@@ -28,14 +23,6 @@ else
|
|||||||
shlwapi_dep = dependency('', required: false)
|
shlwapi_dep = dependency('', required: false)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if zlib_dep.found()
|
|
||||||
fs_sources += [
|
|
||||||
'io/GunzipReader.cxx',
|
|
||||||
'io/AutoGunzipReader.cxx',
|
|
||||||
'io/GzipOutputStream.cxx',
|
|
||||||
]
|
|
||||||
endif
|
|
||||||
|
|
||||||
fs = static_library(
|
fs = static_library(
|
||||||
'fs',
|
'fs',
|
||||||
fs_sources,
|
fs_sources,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "IcyInputStream.hxx"
|
#include "IcyInputStream.hxx"
|
||||||
#include "IcyMetaDataParser.hxx"
|
#include "tag/IcyMetaDataParser.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "util/UriExtract.hxx"
|
#include "util/UriExtract.hxx"
|
||||||
#include "util/UriQueryParser.hxx"
|
#include "util/UriQueryParser.hxx"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#ifndef MPD_INPUT_READER_HXX
|
#ifndef MPD_INPUT_READER_HXX
|
||||||
#define MPD_INPUT_READER_HXX
|
#define MPD_INPUT_READER_HXX
|
||||||
|
|
||||||
#include "fs/io/Reader.hxx"
|
#include "io/Reader.hxx"
|
||||||
|
|
||||||
class InputStream;
|
class InputStream;
|
||||||
|
|
||||||
|
|||||||
@@ -30,10 +30,12 @@
|
|||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
#include "util/ByteOrder.hxx"
|
#include "util/ByteOrder.hxx"
|
||||||
|
#include "util/ScopeExit.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
#include "config/Block.hxx"
|
#include "config/Block.hxx"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -48,21 +50,19 @@ class CdioParanoiaInputStream final : public InputStream {
|
|||||||
CdIo_t *const cdio;
|
CdIo_t *const cdio;
|
||||||
CdromParanoia para;
|
CdromParanoia para;
|
||||||
|
|
||||||
const lsn_t lsn_from, lsn_to;
|
const lsn_t lsn_from;
|
||||||
int lsn_relofs;
|
|
||||||
|
|
||||||
char buffer[CDIO_CD_FRAMESIZE_RAW];
|
char buffer[CDIO_CD_FRAMESIZE_RAW];
|
||||||
int buffer_lsn;
|
lsn_t buffer_lsn;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CdioParanoiaInputStream(const char *_uri, Mutex &_mutex,
|
CdioParanoiaInputStream(const char *_uri, Mutex &_mutex,
|
||||||
cdrom_drive_t *_drv, CdIo_t *_cdio,
|
cdrom_drive_t *_drv, CdIo_t *_cdio,
|
||||||
bool reverse_endian,
|
bool reverse_endian,
|
||||||
lsn_t _lsn_from, lsn_t _lsn_to)
|
lsn_t _lsn_from, lsn_t lsn_to)
|
||||||
:InputStream(_uri, _mutex),
|
:InputStream(_uri, _mutex),
|
||||||
drv(_drv), cdio(_cdio), para(drv),
|
drv(_drv), cdio(_cdio), para(drv),
|
||||||
lsn_from(_lsn_from), lsn_to(_lsn_to),
|
lsn_from(_lsn_from),
|
||||||
lsn_relofs(0),
|
|
||||||
buffer_lsn(-1)
|
buffer_lsn(-1)
|
||||||
{
|
{
|
||||||
/* Set reading mode for full paranoia, but allow
|
/* Set reading mode for full paranoia, but allow
|
||||||
@@ -173,9 +173,12 @@ cdio_detect_device()
|
|||||||
if (devices == nullptr)
|
if (devices == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
AllocatedPath path = AllocatedPath::FromFS(devices[0]);
|
AtScopeExit(devices) { cdio_free_device_list(devices); };
|
||||||
cdio_free_device_list(devices);
|
|
||||||
return path;
|
if (devices[0] == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return AllocatedPath::FromFS(devices[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static InputStreamPtr
|
static InputStreamPtr
|
||||||
@@ -271,29 +274,30 @@ CdioParanoiaInputStream::Seek(std::unique_lock<Mutex> &,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* calculate current LSN */
|
/* calculate current LSN */
|
||||||
lsn_relofs = new_offset / CDIO_CD_FRAMESIZE_RAW;
|
const lsn_t lsn_relofs = new_offset / CDIO_CD_FRAMESIZE_RAW;
|
||||||
offset = new_offset;
|
|
||||||
|
|
||||||
{
|
if (lsn_relofs != buffer_lsn) {
|
||||||
const ScopeUnlock unlock(mutex);
|
const ScopeUnlock unlock(mutex);
|
||||||
para.Seek(lsn_from + lsn_relofs);
|
para.Seek(lsn_from + lsn_relofs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offset = new_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
CdioParanoiaInputStream::Read(std::unique_lock<Mutex> &,
|
CdioParanoiaInputStream::Read(std::unique_lock<Mutex> &,
|
||||||
void *ptr, size_t length)
|
void *ptr, size_t length)
|
||||||
{
|
{
|
||||||
size_t nbytes = 0;
|
|
||||||
char *wptr = (char *) ptr;
|
|
||||||
|
|
||||||
while (length > 0) {
|
|
||||||
/* end of track ? */
|
/* end of track ? */
|
||||||
if (lsn_from + lsn_relofs > lsn_to)
|
if (IsEOF())
|
||||||
break;
|
return 0;
|
||||||
|
|
||||||
//current sector was changed ?
|
//current sector was changed ?
|
||||||
const int16_t *rbuf;
|
const int16_t *rbuf;
|
||||||
|
|
||||||
|
const lsn_t lsn_relofs = offset / CDIO_CD_FRAMESIZE_RAW;
|
||||||
|
const std::size_t diff = offset % CDIO_CD_FRAMESIZE_RAW;
|
||||||
|
|
||||||
if (lsn_relofs != buffer_lsn) {
|
if (lsn_relofs != buffer_lsn) {
|
||||||
const ScopeUnlock unlock(mutex);
|
const ScopeUnlock unlock(mutex);
|
||||||
|
|
||||||
@@ -318,26 +322,14 @@ CdioParanoiaInputStream::Read(std::unique_lock<Mutex> &,
|
|||||||
rbuf = (const int16_t *)buffer;
|
rbuf = (const int16_t *)buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
//correct offset
|
|
||||||
const int diff = offset - lsn_relofs * CDIO_CD_FRAMESIZE_RAW;
|
|
||||||
|
|
||||||
assert(diff >= 0 && diff < CDIO_CD_FRAMESIZE_RAW);
|
|
||||||
|
|
||||||
const size_t maxwrite = CDIO_CD_FRAMESIZE_RAW - diff; //# of bytes pending in current buffer
|
const size_t maxwrite = CDIO_CD_FRAMESIZE_RAW - diff; //# of bytes pending in current buffer
|
||||||
const size_t len = (length < maxwrite? length : maxwrite);
|
const std::size_t nbytes = std::min(length, maxwrite);
|
||||||
|
|
||||||
//skip diff bytes from this lsn
|
//skip diff bytes from this lsn
|
||||||
memcpy(wptr, ((const char *)rbuf) + diff, len);
|
memcpy(ptr, ((const char *)rbuf) + diff, nbytes);
|
||||||
//update pointer
|
|
||||||
wptr += len;
|
|
||||||
nbytes += len;
|
|
||||||
|
|
||||||
//update offset
|
//update offset
|
||||||
offset += len;
|
offset += nbytes;
|
||||||
lsn_relofs = offset / CDIO_CD_FRAMESIZE_RAW;
|
|
||||||
//update length
|
|
||||||
length -= len;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nbytes;
|
return nbytes;
|
||||||
}
|
}
|
||||||
@@ -345,7 +337,7 @@ CdioParanoiaInputStream::Read(std::unique_lock<Mutex> &,
|
|||||||
bool
|
bool
|
||||||
CdioParanoiaInputStream::IsEOF() const noexcept
|
CdioParanoiaInputStream::IsEOF() const noexcept
|
||||||
{
|
{
|
||||||
return lsn_from + lsn_relofs > lsn_to;
|
return offset >= size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr const char *cdio_paranoia_prefixes[] = {
|
static constexpr const char *cdio_paranoia_prefixes[] = {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
#include "../MaybeBufferedInputStream.hxx"
|
#include "../MaybeBufferedInputStream.hxx"
|
||||||
#include "../AsyncInputStream.hxx"
|
#include "../AsyncInputStream.hxx"
|
||||||
#include "../IcyInputStream.hxx"
|
#include "../IcyInputStream.hxx"
|
||||||
#include "IcyMetaDataParser.hxx"
|
#include "tag/IcyMetaDataParser.hxx"
|
||||||
#include "../InputPlugin.hxx"
|
#include "../InputPlugin.hxx"
|
||||||
#include "config/Block.hxx"
|
#include "config/Block.hxx"
|
||||||
#include "tag/Builder.hxx"
|
#include "tag/Builder.hxx"
|
||||||
@@ -82,7 +82,7 @@ class CurlInputStream final : public AsyncInputStream, CurlResponseHandler {
|
|||||||
public:
|
public:
|
||||||
template<typename I>
|
template<typename I>
|
||||||
CurlInputStream(EventLoop &event_loop, const char *_url,
|
CurlInputStream(EventLoop &event_loop, const char *_url,
|
||||||
const std::multimap<std::string, std::string> &headers,
|
const Curl::Headers &headers,
|
||||||
I &&_icy,
|
I &&_icy,
|
||||||
Mutex &_mutex);
|
Mutex &_mutex);
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ public:
|
|||||||
CurlInputStream &operator=(const CurlInputStream &) = delete;
|
CurlInputStream &operator=(const CurlInputStream &) = delete;
|
||||||
|
|
||||||
static InputStreamPtr Open(const char *url,
|
static InputStreamPtr Open(const char *url,
|
||||||
const std::multimap<std::string, std::string> &headers,
|
const Curl::Headers &headers,
|
||||||
Mutex &mutex);
|
Mutex &mutex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -131,8 +131,7 @@ private:
|
|||||||
void SeekInternal(offset_type new_offset);
|
void SeekInternal(offset_type new_offset);
|
||||||
|
|
||||||
/* virtual methods from CurlResponseHandler */
|
/* virtual methods from CurlResponseHandler */
|
||||||
void OnHeaders(unsigned status,
|
void OnHeaders(unsigned status, Curl::Headers &&headers) override;
|
||||||
std::multimap<std::string, std::string> &&headers) override;
|
|
||||||
void OnData(ConstBuffer<void> data) override;
|
void OnData(ConstBuffer<void> data) override;
|
||||||
void OnEnd() override;
|
void OnEnd() override;
|
||||||
void OnError(std::exception_ptr e) noexcept override;
|
void OnError(std::exception_ptr e) noexcept override;
|
||||||
@@ -227,7 +226,7 @@ WithConvertedTagValue(const char *uri, const char *value, F &&f) noexcept
|
|||||||
|
|
||||||
void
|
void
|
||||||
CurlInputStream::OnHeaders(unsigned status,
|
CurlInputStream::OnHeaders(unsigned status,
|
||||||
std::multimap<std::string, std::string> &&headers)
|
Curl::Headers &&headers)
|
||||||
{
|
{
|
||||||
assert(GetEventLoop().IsInside());
|
assert(GetEventLoop().IsInside());
|
||||||
assert(!postponed_exception);
|
assert(!postponed_exception);
|
||||||
@@ -391,7 +390,7 @@ input_curl_finish() noexcept
|
|||||||
template<typename I>
|
template<typename I>
|
||||||
inline
|
inline
|
||||||
CurlInputStream::CurlInputStream(EventLoop &event_loop, const char *_url,
|
CurlInputStream::CurlInputStream(EventLoop &event_loop, const char *_url,
|
||||||
const std::multimap<std::string, std::string> &headers,
|
const Curl::Headers &headers,
|
||||||
I &&_icy,
|
I &&_icy,
|
||||||
Mutex &_mutex)
|
Mutex &_mutex)
|
||||||
:AsyncInputStream(event_loop, _url, _mutex,
|
:AsyncInputStream(event_loop, _url, _mutex,
|
||||||
@@ -491,7 +490,7 @@ CurlInputStream::DoSeek(offset_type new_offset)
|
|||||||
|
|
||||||
inline InputStreamPtr
|
inline InputStreamPtr
|
||||||
CurlInputStream::Open(const char *url,
|
CurlInputStream::Open(const char *url,
|
||||||
const std::multimap<std::string, std::string> &headers,
|
const Curl::Headers &headers,
|
||||||
Mutex &mutex)
|
Mutex &mutex)
|
||||||
{
|
{
|
||||||
auto icy = std::make_shared<IcyMetaDataParser>();
|
auto icy = std::make_shared<IcyMetaDataParser>();
|
||||||
@@ -510,8 +509,7 @@ CurlInputStream::Open(const char *url,
|
|||||||
}
|
}
|
||||||
|
|
||||||
InputStreamPtr
|
InputStreamPtr
|
||||||
OpenCurlInputStream(const char *uri,
|
OpenCurlInputStream(const char *uri, const Curl::Headers &headers,
|
||||||
const std::multimap<std::string, std::string> &headers,
|
|
||||||
Mutex &mutex)
|
Mutex &mutex)
|
||||||
{
|
{
|
||||||
return CurlInputStream::Open(uri, headers, mutex);
|
return CurlInputStream::Open(uri, headers, mutex);
|
||||||
|
|||||||
@@ -20,12 +20,10 @@
|
|||||||
#ifndef MPD_INPUT_CURL_HXX
|
#ifndef MPD_INPUT_CURL_HXX
|
||||||
#define MPD_INPUT_CURL_HXX
|
#define MPD_INPUT_CURL_HXX
|
||||||
|
|
||||||
|
#include "lib/curl/Headers.hxx"
|
||||||
#include "input/Ptr.hxx"
|
#include "input/Ptr.hxx"
|
||||||
#include "thread/Mutex.hxx"
|
#include "thread/Mutex.hxx"
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
extern const struct InputPlugin input_plugin_curl;
|
extern const struct InputPlugin input_plugin_curl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,8 +34,7 @@ extern const struct InputPlugin input_plugin_curl;
|
|||||||
* Throws on error.
|
* Throws on error.
|
||||||
*/
|
*/
|
||||||
InputStreamPtr
|
InputStreamPtr
|
||||||
OpenCurlInputStream(const char *uri,
|
OpenCurlInputStream(const char *uri, const Curl::Headers &headers,
|
||||||
const std::multimap<std::string, std::string> &headers,
|
|
||||||
Mutex &mutex);
|
Mutex &mutex);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#include "../InputStream.hxx"
|
#include "../InputStream.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
#include "fs/FileInfo.hxx"
|
#include "fs/FileInfo.hxx"
|
||||||
#include "fs/io/FileReader.hxx"
|
#include "io/FileReader.hxx"
|
||||||
#include "io/FileDescriptor.hxx"
|
#include "io/FileDescriptor.hxx"
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
|
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ QobuzClient::InvokeHandlers() noexcept
|
|||||||
|
|
||||||
std::string
|
std::string
|
||||||
QobuzClient::MakeUrl(const char *object, const char *method,
|
QobuzClient::MakeUrl(const char *object, const char *method,
|
||||||
const std::multimap<std::string, std::string> &query) const noexcept
|
const Curl::Headers &query) const noexcept
|
||||||
{
|
{
|
||||||
assert(!query.empty());
|
assert(!query.empty());
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ QobuzClient::MakeUrl(const char *object, const char *method,
|
|||||||
|
|
||||||
std::string
|
std::string
|
||||||
QobuzClient::MakeSignedUrl(const char *object, const char *method,
|
QobuzClient::MakeSignedUrl(const char *object, const char *method,
|
||||||
const std::multimap<std::string, std::string> &query) const noexcept
|
const Curl::Headers &query) const noexcept
|
||||||
{
|
{
|
||||||
assert(!query.empty());
|
assert(!query.empty());
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,12 @@
|
|||||||
#include "QobuzSession.hxx"
|
#include "QobuzSession.hxx"
|
||||||
#include "QobuzLoginRequest.hxx"
|
#include "QobuzLoginRequest.hxx"
|
||||||
#include "lib/curl/Init.hxx"
|
#include "lib/curl/Init.hxx"
|
||||||
|
#include "lib/curl/Headers.hxx"
|
||||||
#include "thread/Mutex.hxx"
|
#include "thread/Mutex.hxx"
|
||||||
#include "event/DeferEvent.hxx"
|
#include "event/DeferEvent.hxx"
|
||||||
#include "util/IntrusiveList.hxx"
|
#include "util/IntrusiveList.hxx"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <map>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class QobuzSessionHandler
|
class QobuzSessionHandler
|
||||||
@@ -94,10 +94,10 @@ public:
|
|||||||
QobuzSession GetSession() const;
|
QobuzSession GetSession() const;
|
||||||
|
|
||||||
std::string MakeUrl(const char *object, const char *method,
|
std::string MakeUrl(const char *object, const char *method,
|
||||||
const std::multimap<std::string, std::string> &query) const noexcept;
|
const Curl::Headers &query) const noexcept;
|
||||||
|
|
||||||
std::string MakeSignedUrl(const char *object, const char *method,
|
std::string MakeSignedUrl(const char *object, const char *method,
|
||||||
const std::multimap<std::string, std::string> &query) const noexcept;
|
const Curl::Headers &query) const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void StartLogin();
|
void StartLogin();
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ static constexpr yajl_callbacks qobuz_error_parser_callbacks = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
QobuzErrorParser::QobuzErrorParser(unsigned _status,
|
QobuzErrorParser::QobuzErrorParser(unsigned _status,
|
||||||
const std::multimap<std::string, std::string> &headers)
|
const Curl::Headers &headers)
|
||||||
:YajlResponseParser(&qobuz_error_parser_callbacks, nullptr, this),
|
:YajlResponseParser(&qobuz_error_parser_callbacks, nullptr, this),
|
||||||
status(_status)
|
status(_status)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,11 +20,9 @@
|
|||||||
#ifndef QOBUZ_ERROR_PARSER_HXX
|
#ifndef QOBUZ_ERROR_PARSER_HXX
|
||||||
#define QOBUZ_ERROR_PARSER_HXX
|
#define QOBUZ_ERROR_PARSER_HXX
|
||||||
|
|
||||||
|
#include "lib/curl/Headers.hxx"
|
||||||
#include "lib/yajl/ResponseParser.hxx"
|
#include "lib/yajl/ResponseParser.hxx"
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
template<typename T> struct ConstBuffer;
|
template<typename T> struct ConstBuffer;
|
||||||
struct StringView;
|
struct StringView;
|
||||||
|
|
||||||
@@ -46,8 +44,7 @@ public:
|
|||||||
* May throw if there is a formal error in the response
|
* May throw if there is a formal error in the response
|
||||||
* headers.
|
* headers.
|
||||||
*/
|
*/
|
||||||
QobuzErrorParser(unsigned status,
|
QobuzErrorParser(unsigned status, const Curl::Headers &headers);
|
||||||
const std::multimap<std::string, std::string> &headers);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/* virtual methods from CurlResponseParser */
|
/* virtual methods from CurlResponseParser */
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ QobuzLoginRequest::ResponseParser::GetSession()
|
|||||||
return std::move(session);
|
return std::move(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::multimap<std::string, std::string>
|
static Curl::Headers
|
||||||
MakeLoginForm(const char *app_id,
|
MakeLoginForm(const char *app_id,
|
||||||
const char *username, const char *email,
|
const char *username, const char *email,
|
||||||
const char *password,
|
const char *password,
|
||||||
@@ -85,7 +85,7 @@ MakeLoginForm(const char *app_id,
|
|||||||
{
|
{
|
||||||
assert(username != nullptr || email != nullptr);
|
assert(username != nullptr || email != nullptr);
|
||||||
|
|
||||||
std::multimap<std::string, std::string> form{
|
Curl::Headers form{
|
||||||
{"app_id", app_id},
|
{"app_id", app_id},
|
||||||
{"password", password},
|
{"password", password},
|
||||||
{"device_manufacturer_id", device_manufacturer_id},
|
{"device_manufacturer_id", device_manufacturer_id},
|
||||||
@@ -134,8 +134,7 @@ QobuzLoginRequest::~QobuzLoginRequest() noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<CurlResponseParser>
|
std::unique_ptr<CurlResponseParser>
|
||||||
QobuzLoginRequest::MakeParser(unsigned status,
|
QobuzLoginRequest::MakeParser(unsigned status, Curl::Headers &&headers)
|
||||||
std::multimap<std::string, std::string> &&headers)
|
|
||||||
{
|
{
|
||||||
if (status != 200)
|
if (status != 200)
|
||||||
return std::make_unique<QobuzErrorParser>(status, headers);
|
return std::make_unique<QobuzErrorParser>(status, headers);
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
/* virtual methods from DelegateCurlResponseHandler */
|
/* virtual methods from DelegateCurlResponseHandler */
|
||||||
std::unique_ptr<CurlResponseParser> MakeParser(unsigned status,
|
std::unique_ptr<CurlResponseParser> MakeParser(unsigned status,
|
||||||
std::multimap<std::string, std::string> &&headers) override;
|
Curl::Headers &&headers) override;
|
||||||
void FinishParser(std::unique_ptr<CurlResponseParser> p) override;
|
void FinishParser(std::unique_ptr<CurlResponseParser> p) override;
|
||||||
|
|
||||||
/* virtual methods from CurlResponseHandler */
|
/* virtual methods from CurlResponseHandler */
|
||||||
|
|||||||
@@ -99,8 +99,7 @@ QobuzTagScanner::~QobuzTagScanner() noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<CurlResponseParser>
|
std::unique_ptr<CurlResponseParser>
|
||||||
QobuzTagScanner::MakeParser(unsigned status,
|
QobuzTagScanner::MakeParser(unsigned status, Curl::Headers &&headers)
|
||||||
std::multimap<std::string, std::string> &&headers)
|
|
||||||
{
|
{
|
||||||
if (status != 200)
|
if (status != 200)
|
||||||
return std::make_unique<QobuzErrorParser>(status, headers);
|
return std::make_unique<QobuzErrorParser>(status, headers);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
/* virtual methods from DelegateCurlResponseHandler */
|
/* virtual methods from DelegateCurlResponseHandler */
|
||||||
std::unique_ptr<CurlResponseParser> MakeParser(unsigned status,
|
std::unique_ptr<CurlResponseParser> MakeParser(unsigned status,
|
||||||
std::multimap<std::string, std::string> &&headers) override;
|
Curl::Headers &&headers) override;
|
||||||
void FinishParser(std::unique_ptr<CurlResponseParser> p) override;
|
void FinishParser(std::unique_ptr<CurlResponseParser> p) override;
|
||||||
|
|
||||||
/* virtual methods from CurlResponseHandler */
|
/* virtual methods from CurlResponseHandler */
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ QobuzTrackRequest::~QobuzTrackRequest() noexcept
|
|||||||
|
|
||||||
std::unique_ptr<CurlResponseParser>
|
std::unique_ptr<CurlResponseParser>
|
||||||
QobuzTrackRequest::MakeParser(unsigned status,
|
QobuzTrackRequest::MakeParser(unsigned status,
|
||||||
std::multimap<std::string, std::string> &&headers)
|
Curl::Headers &&headers)
|
||||||
{
|
{
|
||||||
if (status != 200)
|
if (status != 200)
|
||||||
return std::make_unique<QobuzErrorParser>(status, headers);
|
return std::make_unique<QobuzErrorParser>(status, headers);
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
/* virtual methods from DelegateCurlResponseHandler */
|
/* virtual methods from DelegateCurlResponseHandler */
|
||||||
std::unique_ptr<CurlResponseParser> MakeParser(unsigned status,
|
std::unique_ptr<CurlResponseParser> MakeParser(unsigned status,
|
||||||
std::multimap<std::string, std::string> &&headers) override;
|
Curl::Headers &&headers) override;
|
||||||
void FinishParser(std::unique_ptr<CurlResponseParser> p) override;
|
void FinishParser(std::unique_ptr<CurlResponseParser> p) override;
|
||||||
|
|
||||||
/* virtual methods from CurlResponseHandler */
|
/* virtual methods from CurlResponseHandler */
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ if curl_dep.found()
|
|||||||
input_plugins_sources += [
|
input_plugins_sources += [
|
||||||
'CurlInputPlugin.cxx',
|
'CurlInputPlugin.cxx',
|
||||||
'../IcyInputStream.cxx',
|
'../IcyInputStream.cxx',
|
||||||
'../../IcyMetaDataParser.cxx',
|
'../../tag/IcyMetaDataParser.cxx',
|
||||||
]
|
]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|||||||
33
src/io/LineReader.hxx
Normal file
33
src/io/LineReader.hxx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2021 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class LineReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Reads a line from the input file, and strips trailing
|
||||||
|
* space. There is a reasonable maximum line length, only to
|
||||||
|
* prevent denial of service.
|
||||||
|
*
|
||||||
|
* @return a pointer to the line, or nullptr on end-of-file
|
||||||
|
*/
|
||||||
|
virtual char *ReadLine() = 0;
|
||||||
|
};
|
||||||
@@ -2,6 +2,11 @@ io = static_library(
|
|||||||
'io',
|
'io',
|
||||||
'FileDescriptor.cxx',
|
'FileDescriptor.cxx',
|
||||||
'Open.cxx',
|
'Open.cxx',
|
||||||
|
'PeekReader.cxx',
|
||||||
|
'FileReader.cxx',
|
||||||
|
'BufferedReader.cxx',
|
||||||
|
'FileOutputStream.cxx',
|
||||||
|
'BufferedOutputStream.cxx',
|
||||||
include_directories: inc,
|
include_directories: inc,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -45,10 +45,15 @@ class Operation {
|
|||||||
CancellableOperation *cancellable = nullptr;
|
CancellableOperation *cancellable = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Operation() noexcept = default;
|
||||||
|
|
||||||
~Operation() noexcept {
|
~Operation() noexcept {
|
||||||
CancelUring();
|
CancelUring();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Operation(const Operation &) = delete;
|
||||||
|
Operation &operator=(const Operation &) = delete;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Are we waiting for the operation to complete?
|
* Are we waiting for the operation to complete?
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
#include "CancellableOperation.hxx"
|
#include "CancellableOperation.hxx"
|
||||||
#include "util/DeleteDisposer.hxx"
|
#include "util/DeleteDisposer.hxx"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace Uring {
|
namespace Uring {
|
||||||
|
|
||||||
Queue::Queue(unsigned entries, unsigned flags)
|
Queue::Queue(unsigned entries, unsigned flags)
|
||||||
@@ -46,6 +48,23 @@ Queue::~Queue() noexcept
|
|||||||
operations.clear_and_dispose(DeleteDisposer{});
|
operations.clear_and_dispose(DeleteDisposer{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct io_uring_sqe &
|
||||||
|
Queue::RequireSubmitEntry()
|
||||||
|
{
|
||||||
|
auto *sqe = GetSubmitEntry();
|
||||||
|
if (sqe == nullptr) {
|
||||||
|
/* the submit queue is full; submit it to the kernel
|
||||||
|
and try again */
|
||||||
|
Submit();
|
||||||
|
|
||||||
|
sqe = GetSubmitEntry();
|
||||||
|
if (sqe == nullptr)
|
||||||
|
throw std::runtime_error{"io_uring_get_sqe() failed"};
|
||||||
|
}
|
||||||
|
|
||||||
|
return *sqe;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Queue::AddPending(struct io_uring_sqe &sqe,
|
Queue::AddPending(struct io_uring_sqe &sqe,
|
||||||
Operation &operation) noexcept
|
Operation &operation) noexcept
|
||||||
|
|||||||
@@ -63,6 +63,14 @@ public:
|
|||||||
return ring.GetSubmitEntry();
|
return ring.GetSubmitEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like GetSubmitEntry(), but call Submit() if the submit
|
||||||
|
* queue is full.
|
||||||
|
*
|
||||||
|
* May throw exceptions if Submit() fails.
|
||||||
|
*/
|
||||||
|
struct io_uring_sqe &RequireSubmitEntry();
|
||||||
|
|
||||||
bool HasPending() const noexcept {
|
bool HasPending() const noexcept {
|
||||||
return !operations.empty();
|
return !operations.empty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,14 +45,13 @@ ReadOperation::Start(Queue &queue, FileDescriptor fd, off_t offset,
|
|||||||
|
|
||||||
buffer = std::make_unique<std::byte[]>(size);
|
buffer = std::make_unique<std::byte[]>(size);
|
||||||
|
|
||||||
auto *s = queue.GetSubmitEntry();
|
auto &s = queue.RequireSubmitEntry();
|
||||||
assert(s != nullptr); // TODO: what if the submit queue is full?
|
|
||||||
|
|
||||||
iov.iov_base = buffer.get();
|
iov.iov_base = buffer.get();
|
||||||
iov.iov_len = size;
|
iov.iov_len = size;
|
||||||
|
|
||||||
io_uring_prep_readv(s, fd.Get(), &iov, 1, offset);
|
io_uring_prep_readv(&s, fd.Get(), &iov, 1, offset);
|
||||||
queue.Push(*s, *this);
|
queue.Push(s, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include "Error.hxx"
|
#include "Error.hxx"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
#include <alsa/error.h>
|
#include <alsa/error.h>
|
||||||
|
|
||||||
namespace Alsa {
|
namespace Alsa {
|
||||||
|
|||||||
@@ -50,4 +50,4 @@ MakeError(int error, const char *msg) noexcept
|
|||||||
return std::system_error(error, error_category, msg);
|
return std::system_error(error, error_category, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Avahi
|
} // namespace Alsa
|
||||||
|
|||||||
204
src/lib/curl/Adapter.cxx
Normal file
204
src/lib/curl/Adapter.cxx
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008-2021 Max Kellermann <max.kellermann@gmail.com>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Adapter.hxx"
|
||||||
|
#include "Easy.hxx"
|
||||||
|
#include "Handler.hxx"
|
||||||
|
#include "util/CharUtil.hxx"
|
||||||
|
#include "util/RuntimeError.hxx"
|
||||||
|
#include "util/StringStrip.hxx"
|
||||||
|
#include "util/StringView.hxx"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
void
|
||||||
|
CurlResponseHandlerAdapter::Install(CurlEasy &easy)
|
||||||
|
{
|
||||||
|
assert(state == State::UNINITIALISED);
|
||||||
|
|
||||||
|
error_buffer[0] = 0;
|
||||||
|
easy.SetErrorBuffer(error_buffer);
|
||||||
|
|
||||||
|
easy.SetHeaderFunction(_HeaderFunction, this);
|
||||||
|
easy.SetWriteFunction(WriteFunction, this);
|
||||||
|
|
||||||
|
curl = easy.Get();
|
||||||
|
|
||||||
|
state = State::HEADERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CurlResponseHandlerAdapter::FinishHeaders()
|
||||||
|
{
|
||||||
|
assert(state >= State::HEADERS);
|
||||||
|
|
||||||
|
if (state != State::HEADERS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state = State::BODY;
|
||||||
|
|
||||||
|
long status = 0;
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
|
||||||
|
|
||||||
|
handler.OnHeaders(status, std::move(headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CurlResponseHandlerAdapter::FinishBody()
|
||||||
|
{
|
||||||
|
FinishHeaders();
|
||||||
|
|
||||||
|
if (state != State::BODY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state = State::CLOSED;
|
||||||
|
handler.OnEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CurlResponseHandlerAdapter::Done(CURLcode result) noexcept
|
||||||
|
{
|
||||||
|
if (postponed_error) {
|
||||||
|
state = State::CLOSED;
|
||||||
|
handler.OnError(std::move(postponed_error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (result != CURLE_OK) {
|
||||||
|
StripRight(error_buffer);
|
||||||
|
const char *msg = error_buffer;
|
||||||
|
if (*msg == 0)
|
||||||
|
msg = curl_easy_strerror(result);
|
||||||
|
throw FormatRuntimeError("CURL failed: %s", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
FinishBody();
|
||||||
|
} catch (...) {
|
||||||
|
state = State::CLOSED;
|
||||||
|
handler.OnError(std::current_exception());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[gnu::pure]]
|
||||||
|
static bool
|
||||||
|
IsResponseBoundaryHeader(StringView s) noexcept
|
||||||
|
{
|
||||||
|
return s.size > 5 && (s.StartsWith("HTTP/") ||
|
||||||
|
/* the proprietary "ICY 200 OK" is
|
||||||
|
emitted by Shoutcast */
|
||||||
|
s.StartsWith("ICY 2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
CurlResponseHandlerAdapter::HeaderFunction(StringView s) noexcept
|
||||||
|
{
|
||||||
|
if (state > State::HEADERS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (IsResponseBoundaryHeader(s)) {
|
||||||
|
/* this is the boundary to a new response, for example
|
||||||
|
after a redirect */
|
||||||
|
headers.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *header = s.data;
|
||||||
|
const char *end = StripRight(header, header + s.size);
|
||||||
|
|
||||||
|
const char *value = s.Find(':');
|
||||||
|
if (value == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string name(header, value);
|
||||||
|
std::transform(name.begin(), name.end(), name.begin(),
|
||||||
|
static_cast<char(*)(char)>(ToLowerASCII));
|
||||||
|
|
||||||
|
/* skip the colon */
|
||||||
|
|
||||||
|
++value;
|
||||||
|
|
||||||
|
/* strip the value */
|
||||||
|
|
||||||
|
value = StripLeft(value, end);
|
||||||
|
end = StripRight(value, end);
|
||||||
|
|
||||||
|
headers.emplace(std::move(name), std::string(value, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
CurlResponseHandlerAdapter::_HeaderFunction(char *ptr, std::size_t size,
|
||||||
|
std::size_t nmemb,
|
||||||
|
void *stream) noexcept
|
||||||
|
{
|
||||||
|
CurlResponseHandlerAdapter &c = *(CurlResponseHandlerAdapter *)stream;
|
||||||
|
|
||||||
|
size *= nmemb;
|
||||||
|
|
||||||
|
c.HeaderFunction({ptr, size});
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::size_t
|
||||||
|
CurlResponseHandlerAdapter::DataReceived(const void *ptr,
|
||||||
|
std::size_t received_size) noexcept
|
||||||
|
{
|
||||||
|
assert(received_size > 0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
FinishHeaders();
|
||||||
|
handler.OnData({ptr, received_size});
|
||||||
|
return received_size;
|
||||||
|
} catch (CurlResponseHandler::Pause) {
|
||||||
|
return CURL_WRITEFUNC_PAUSE;
|
||||||
|
} catch (...) {
|
||||||
|
/* from inside this libCURL callback function, we
|
||||||
|
can't do much, so we remember the exception to be
|
||||||
|
handled later by Done(), and return 0, causing the
|
||||||
|
response to be aborted with CURLE_WRITE_ERROR */
|
||||||
|
postponed_error = std::current_exception();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
CurlResponseHandlerAdapter::WriteFunction(char *ptr, std::size_t size,
|
||||||
|
std::size_t nmemb,
|
||||||
|
void *stream) noexcept
|
||||||
|
{
|
||||||
|
CurlResponseHandlerAdapter &c = *(CurlResponseHandlerAdapter *)stream;
|
||||||
|
|
||||||
|
size *= nmemb;
|
||||||
|
if (size == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return c.DataReceived(ptr, size);
|
||||||
|
}
|
||||||
91
src/lib/curl/Adapter.hxx
Normal file
91
src/lib/curl/Adapter.hxx
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008-2022 Max Kellermann <max.kellermann@gmail.com>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Headers.hxx"
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
struct StringView;
|
||||||
|
class CurlEasy;
|
||||||
|
class CurlResponseHandler;
|
||||||
|
|
||||||
|
class CurlResponseHandlerAdapter {
|
||||||
|
CURL *curl;
|
||||||
|
|
||||||
|
CurlResponseHandler &handler;
|
||||||
|
|
||||||
|
Curl::Headers headers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception caught from within the WriteFunction() which
|
||||||
|
* will later be handled by Done().
|
||||||
|
*/
|
||||||
|
std::exception_ptr postponed_error;
|
||||||
|
|
||||||
|
/** error message provided by libcurl */
|
||||||
|
char error_buffer[CURL_ERROR_SIZE];
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
UNINITIALISED,
|
||||||
|
HEADERS,
|
||||||
|
BODY,
|
||||||
|
CLOSED,
|
||||||
|
} state = State::UNINITIALISED;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CurlResponseHandlerAdapter(CurlResponseHandler &_handler) noexcept
|
||||||
|
:handler(_handler) {}
|
||||||
|
|
||||||
|
void Install(CurlEasy &easy);
|
||||||
|
|
||||||
|
void Done(CURLcode result) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void FinishHeaders();
|
||||||
|
void FinishBody();
|
||||||
|
|
||||||
|
void HeaderFunction(StringView s) noexcept;
|
||||||
|
|
||||||
|
/** called by curl when a new header is available */
|
||||||
|
static std::size_t _HeaderFunction(char *ptr,
|
||||||
|
std::size_t size, std::size_t nmemb,
|
||||||
|
void *stream) noexcept;
|
||||||
|
|
||||||
|
std::size_t DataReceived(const void *ptr, std::size_t size) noexcept;
|
||||||
|
|
||||||
|
/** called by curl when new data is available */
|
||||||
|
static std::size_t WriteFunction(char *ptr,
|
||||||
|
std::size_t size, std::size_t nmemb,
|
||||||
|
void *stream) noexcept;
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2018 Max Kellermann <max.kellermann@gmail.com>
|
* Copyright 2008-2022 Max Kellermann <max.kellermann@gmail.com>
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
@@ -34,8 +34,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
void
|
void
|
||||||
DelegateCurlResponseHandler::OnHeaders(unsigned status,
|
DelegateCurlResponseHandler::OnHeaders(unsigned status, Curl::Headers &&headers)
|
||||||
std::multimap<std::string, std::string> &&headers)
|
|
||||||
{
|
{
|
||||||
parser = MakeParser(status, std::move(headers));
|
parser = MakeParser(status, std::move(headers));
|
||||||
assert(parser);
|
assert(parser);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2018 Max Kellermann <max.kellermann@gmail.com>
|
* Copyright 2008-2022 Max Kellermann <max.kellermann@gmail.com>
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions
|
* modification, are permitted provided that the following conditions
|
||||||
@@ -27,8 +27,7 @@
|
|||||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CURL_DELEGATE_HXX
|
#pragma once
|
||||||
#define CURL_DELEGATE_HXX
|
|
||||||
|
|
||||||
#include "Handler.hxx"
|
#include "Handler.hxx"
|
||||||
|
|
||||||
@@ -53,7 +52,7 @@ protected:
|
|||||||
* CurlResponseParser::OnError()).
|
* CurlResponseParser::OnError()).
|
||||||
*/
|
*/
|
||||||
virtual std::unique_ptr<CurlResponseParser> MakeParser(unsigned status,
|
virtual std::unique_ptr<CurlResponseParser> MakeParser(unsigned status,
|
||||||
std::multimap<std::string, std::string> &&headers) = 0;
|
Curl::Headers &&headers) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The parser has finished parsing the response body. This
|
* The parser has finished parsing the response body. This
|
||||||
@@ -64,10 +63,7 @@ protected:
|
|||||||
virtual void FinishParser(std::unique_ptr<CurlResponseParser> p) = 0;
|
virtual void FinishParser(std::unique_ptr<CurlResponseParser> p) = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void OnHeaders(unsigned status,
|
void OnHeaders(unsigned status, Curl::Headers &&headers) final;
|
||||||
std::multimap<std::string, std::string> &&headers) final;
|
|
||||||
void OnData(ConstBuffer<void> data) final;
|
void OnData(ConstBuffer<void> data) final;
|
||||||
void OnEnd() final;
|
void OnEnd() final;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ CurlUnescape(CURL *curl, StringView src) noexcept
|
|||||||
int outlength;
|
int outlength;
|
||||||
CurlString tmp(curl_easy_unescape(curl, src.data, src.size,
|
CurlString tmp(curl_easy_unescape(curl, src.data, src.size,
|
||||||
&outlength));
|
&outlength));
|
||||||
return std::string(tmp.c_str(), outlength);
|
return {tmp.c_str(), size_t(outlength)};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
|
|||||||
@@ -31,8 +31,7 @@
|
|||||||
#include "String.hxx"
|
#include "String.hxx"
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
EncodeForm(CURL *curl,
|
EncodeForm(CURL *curl, const Curl::Headers &fields) noexcept
|
||||||
const std::multimap<std::string, std::string> &fields) noexcept
|
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
|
|||||||
@@ -30,17 +30,17 @@
|
|||||||
#ifndef CURL_FORM_HXX
|
#ifndef CURL_FORM_HXX
|
||||||
#define CURL_FORM_HXX
|
#define CURL_FORM_HXX
|
||||||
|
|
||||||
|
#include "Headers.hxx"
|
||||||
|
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode the given map of form fields to a
|
* Encode the given map of form fields to a
|
||||||
* "application/x-www-form-urlencoded" string.
|
* "application/x-www-form-urlencoded" string.
|
||||||
*/
|
*/
|
||||||
std::string
|
std::string
|
||||||
EncodeForm(CURL *curl,
|
EncodeForm(CURL *curl, const Curl::Headers &fields) noexcept;
|
||||||
const std::multimap<std::string, std::string> &fields) noexcept;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user