Compare commits
244 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9866adff95 | ||
![]() |
a8b0c55818 | ||
![]() |
cac88e8be5 | ||
![]() |
e9f6a3482c | ||
![]() |
5d2e80f188 | ||
![]() |
cfd4d5b13e | ||
![]() |
06514aec63 | ||
![]() |
4ded1ae67b | ||
![]() |
1da974e3fa | ||
![]() |
94f06f0946 | ||
![]() |
d9eec8a455 | ||
![]() |
eaecbcafb2 | ||
![]() |
73b5d0a9b9 | ||
![]() |
c2d0f35e7a | ||
![]() |
ab99a57997 | ||
![]() |
c8ebaf3521 | ||
![]() |
52d00f7e30 | ||
![]() |
309491a6d8 | ||
![]() |
e7bfd32ccc | ||
![]() |
6f283b52ab | ||
![]() |
32bddfabea | ||
![]() |
1944c826bc | ||
![]() |
619bb60b26 | ||
![]() |
c549e16ed1 | ||
![]() |
01c9c4507f | ||
![]() |
8c9d7bf07e | ||
![]() |
44ef34db88 | ||
![]() |
5781f223f6 | ||
![]() |
e4c8ebe056 | ||
![]() |
76b25a1377 | ||
![]() |
ccc3ee663b | ||
![]() |
0626661764 | ||
![]() |
31db04a3ca | ||
![]() |
0c7163b9db | ||
![]() |
7d78cad8af | ||
![]() |
912530ed20 | ||
![]() |
d3f37199b9 | ||
![]() |
a4748d84b0 | ||
![]() |
8f847ec381 | ||
![]() |
3a70f09dd3 | ||
![]() |
568f63100b | ||
![]() |
3e25916b37 | ||
![]() |
5f9438dae6 | ||
![]() |
99e65c58ce | ||
![]() |
df71b07e9d | ||
![]() |
2694195215 | ||
![]() |
66450d1f3c | ||
![]() |
76efea3aa7 | ||
![]() |
7ab0dfc8ce | ||
![]() |
15ff7c4cad | ||
![]() |
9ab9b97f20 | ||
![]() |
88d92aceab | ||
![]() |
a2ce4352c8 | ||
![]() |
84f43ccde8 | ||
![]() |
38704c9cf3 | ||
![]() |
910d0ec92b | ||
![]() |
3b05c89765 | ||
![]() |
e77b3fa46f | ||
![]() |
12147f6d58 | ||
![]() |
40bc60d6ae | ||
![]() |
7778210269 | ||
![]() |
6229210d51 | ||
![]() |
5d0d5b5d97 | ||
![]() |
1aa3c1e543 | ||
![]() |
b90e32fe4e | ||
![]() |
1f4df2a64d | ||
![]() |
2efc1db6a9 | ||
![]() |
e2d4654e20 | ||
![]() |
2b8f1170a6 | ||
![]() |
5c4743441e | ||
![]() |
cb288439a4 | ||
![]() |
69f741e8a6 | ||
![]() |
4b4f47002b | ||
![]() |
615c301961 | ||
![]() |
dc07180e48 | ||
![]() |
d3b235bab5 | ||
![]() |
7c920ddebe | ||
![]() |
bbc088ae4e | ||
![]() |
fe195257d8 | ||
![]() |
57d5df8118 | ||
![]() |
59792cb0b8 | ||
![]() |
cc557c4d60 | ||
![]() |
956c5faebb | ||
![]() |
cd0396c1f1 | ||
![]() |
79f9b268bb | ||
![]() |
b45f3c8deb | ||
![]() |
f8a8de87e4 | ||
![]() |
2183f0553c | ||
![]() |
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 |
.github
NEWSandroid
doc
meson.buildpython/build
src
LocateUri.cxxLog.hxxLogInit.cxxMain.cxxPartition.cxxPartition.hxxPlaylistDatabase.cxxPlaylistDatabase.hxxPlaylistFile.cxxPlaylistSave.cxxSingleMode.hxxSongSave.cxxSongSave.hxxStateFile.cxxStateFileConfig.cxxTagAny.cxxTagSave.cxx
android
apple
archive
client
command
config
db
Configured.cxx
plugins
decoder
encoder
filter
plugins
fs
input
io
BufferedOutputStream.cxxBufferedOutputStream.hxxBufferedReader.cxxBufferedReader.hxxFileOutputStream.cxxFileOutputStream.hxxFileReader.cxxFileReader.hxxLineReader.hxxOutputStream.hxxPeekReader.cxxPeekReader.hxxReader.hxxStdioOutputStream.hxxmeson.build
uring
java
lib
alsa
curl
Adapter.cxxAdapter.hxxDelegate.cxxDelegate.hxxEscape.cxxForm.cxxForm.hxxHandler.hxxHeaders.hxxRequest.cxxRequest.hxxSetup.cxxSetup.hxxmeson.build
patches
ffmpeg
icu
nfs
patches
upnp
xiph
zlib
mixer
output
pcm
queue
storage
system
tag
time
util
subprojects
test
ContainerScan.cxxDumpOgg.cxxRunCurl.cxxWriteFile.cxxdump_playlist.cxxmeson.build
net
run_decoder.cxxrun_encoder.cxxrun_gunzip.cxxrun_gzip.cxxrun_input.cxxrun_normalize.cxxtest_icy_parser.cxxtest_translate_song.cxxtest_vorbis_encoder.cxxwin32
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 -->
|
||||
|
||||
|
||||
## Configuration
|
||||
<!-- Paste your MPD configuration here -->
|
||||
|
||||
|
||||
## Log
|
||||
<!-- 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/**'
|
||||
branches:
|
||||
- master
|
||||
- actions
|
||||
- v0.23.x
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'android/**'
|
||||
@@ -24,6 +24,7 @@ on:
|
||||
- 'win32/**'
|
||||
branches:
|
||||
- master
|
||||
- v0.23.x
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
@@ -40,7 +41,8 @@ jobs:
|
||||
key: linux
|
||||
- name: Install dependencies
|
||||
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 \
|
||||
libgtest-dev \
|
||||
libpcre2-dev \
|
||||
@@ -72,19 +74,30 @@ jobs:
|
||||
libgcrypt20-dev
|
||||
|
||||
- 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
|
||||
with:
|
||||
action: test
|
||||
directory: output/full
|
||||
setup-options: -Ddocumentation=disabled -Dtest=true -Dsystemd=enabled -Dpcre=enabled
|
||||
options: --verbose
|
||||
meson-version: 0.56.0
|
||||
|
||||
- name: Mini Build
|
||||
uses: BSFishy/meson-build@v1.0.3
|
||||
with:
|
||||
action: test
|
||||
action: build
|
||||
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
|
||||
options: --verbose
|
||||
meson-version: 0.56.0
|
||||
|
||||
build-macos:
|
||||
@@ -123,10 +136,20 @@ jobs:
|
||||
wavpack \
|
||||
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
|
||||
with:
|
||||
action: test
|
||||
directory: output
|
||||
setup-options: -Ddocumentation=disabled -Dtest=true
|
||||
options: --verbose
|
||||
meson-version: 0.56.0
|
||||
|
83
NEWS
83
NEWS
@@ -1,3 +1,86 @@
|
||||
ver 0.23.11 (2022/11/28)
|
||||
* database
|
||||
- simple: move default database to ~/.cache/mpd/db from ~/.cache/mpd.db
|
||||
- simple: default "cache_directory" to ~/.cache/mpd/mounts
|
||||
* macOS: fix build failure "no archive members specified"
|
||||
* Windows
|
||||
- fix crash bug (stack buffer overflow) after I/O errors
|
||||
- fix path traversal bug because backslash was allowed in playlist names
|
||||
* Android/Windows
|
||||
- update OpenSSL to 3.0.7
|
||||
- re-enable CURL's verbose error strings
|
||||
|
||||
ver 0.23.10 (2022/10/14)
|
||||
* storage
|
||||
- curl: fix file time stamps
|
||||
* decoder
|
||||
- ffmpeg: fix libfmt 9 compiler warning
|
||||
* encoder
|
||||
- flac: fix failure when libFLAC is built without Ogg support
|
||||
* output
|
||||
- alsa: fix crash bug
|
||||
* Windows
|
||||
- log to stdout by default, don't require "log_file" setting
|
||||
|
||||
ver 0.23.9 (2022/08/18)
|
||||
* input
|
||||
- cdio_paranoia: add options "mode" and "skip"
|
||||
* decoder
|
||||
- ffmpeg: support FFmpeg 5.1
|
||||
* filter
|
||||
- replay gain: fix delayed volume display with handler=mixer
|
||||
* output
|
||||
- pipewire: set app icon
|
||||
* fix bogus volume levels with multiple partitions
|
||||
* improve iconv detection
|
||||
* macOS: fix macOS 10 build problem (0.23.8 regression)
|
||||
* Android
|
||||
- load mpd.conf from app data directory
|
||||
|
||||
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)
|
||||
* protocol
|
||||
- support relative offsets for "searchadd"
|
||||
|
@@ -2,10 +2,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="65"
|
||||
android:versionName="0.23.5">
|
||||
android:versionCode="70"
|
||||
android:versionName="0.23.11">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
|
||||
|
||||
<uses-feature android:name="android.software.leanback"
|
||||
android:required="false" />
|
||||
@@ -17,10 +17,9 @@
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<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"
|
||||
android:debuggable="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:icon="@drawable/icon"
|
||||
android:banner="@drawable/icon"
|
||||
@@ -43,7 +42,6 @@
|
||||
<receiver android:name=".Receiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.HEADSET_PLUG" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service android:name=".Main" android:process=":main"/>
|
||||
|
@@ -12,18 +12,30 @@ unsigned_apk = custom_target(
|
||||
],
|
||||
)
|
||||
|
||||
aligned_apk = custom_target(
|
||||
'mpd-aligned.apk',
|
||||
output: 'mpd-aligned.apk',
|
||||
input: unsigned_apk,
|
||||
command: [
|
||||
android_zipalign,
|
||||
'-f', '4',
|
||||
'@INPUT@', '@OUTPUT@',
|
||||
],
|
||||
)
|
||||
|
||||
if get_option('android_debug_keystore') != ''
|
||||
debug_apk = custom_target(
|
||||
'mpd-debug.apk',
|
||||
output: 'mpd-debug.apk',
|
||||
input: unsigned_apk,
|
||||
input: aligned_apk,
|
||||
command: [
|
||||
jarsigner,
|
||||
'-keystore', get_option('android_debug_keystore'),
|
||||
'-storepass', 'android',
|
||||
'-signedjar', '@OUTPUT@',
|
||||
'@INPUT@',
|
||||
'androiddebugkey',
|
||||
apksigner, 'sign',
|
||||
'--in', '@INPUT@',
|
||||
'--out', '@OUTPUT@',
|
||||
'--debuggable-apk-permitted',
|
||||
'-ks', get_option('android_debug_keystore'),
|
||||
'--ks-key-alias', 'androiddebugkey',
|
||||
'--ks-pass', 'pass:android',
|
||||
],
|
||||
build_by_default: true
|
||||
)
|
||||
@@ -31,29 +43,16 @@ endif
|
||||
|
||||
if get_option('android_keystore') != '' and get_option('android_keyalias') != '' and get_option('android_keypass') != ''
|
||||
unaligned_apk = custom_target(
|
||||
'mpd-unaligned.apk',
|
||||
output: 'mpd-unaligned.apk',
|
||||
input: unsigned_apk,
|
||||
command: [
|
||||
jarsigner,
|
||||
'-digestalg', 'SHA1', '-sigalg', 'MD5withRSA',
|
||||
'-keystore', get_option('android_keystore'),
|
||||
'-storepass', get_option('android_keypass'),
|
||||
'-signedjar', '@OUTPUT@',
|
||||
'@INPUT@',
|
||||
get_option('android_keyalias'),
|
||||
],
|
||||
)
|
||||
|
||||
apk = custom_target(
|
||||
'mpd.apk',
|
||||
output: 'mpd.apk',
|
||||
input: unaligned_apk,
|
||||
input: aligned_apk,
|
||||
command: [
|
||||
android_zipalign,
|
||||
'-f', '4',
|
||||
'@INPUT@', '@OUTPUT@',
|
||||
apksigner, 'sign',
|
||||
'--in', '@INPUT@',
|
||||
'--out', '@OUTPUT@',
|
||||
'-ks', get_option('android_keystore'),
|
||||
'--ks-key-alias', get_option('android_keyalias'),
|
||||
'--ks-pass', 'pass:' + get_option('android_keypass'),
|
||||
],
|
||||
build_by_default: true
|
||||
)
|
||||
endif
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env -S python3 -u
|
||||
|
||||
import os, os.path
|
||||
import sys, subprocess
|
||||
@@ -13,7 +13,7 @@ android_abi = sys.argv[3]
|
||||
configure_args = sys.argv[4:]
|
||||
|
||||
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)
|
||||
|
||||
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)
|
@@ -17,7 +17,7 @@ android_dx = join_paths(android_build_tools_dir, 'dx')
|
||||
android_zipalign = join_paths(android_build_tools_dir, 'zipalign')
|
||||
|
||||
javac = find_program('javac')
|
||||
jarsigner = find_program('jarsigner')
|
||||
apksigner = find_program('apksigner')
|
||||
rsvg_convert = find_program('rsvg-convert')
|
||||
convert = find_program('convert')
|
||||
zip = find_program('zip')
|
||||
|
@@ -24,14 +24,13 @@ import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
@@ -200,24 +199,14 @@ public class Main extends Service implements Runnable {
|
||||
return;
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_HEADSET_PLUG);
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||
filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
|
||||
registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!mPauseOnHeadphonesDisconnect) {
|
||||
if (!mPauseOnHeadphonesDisconnect)
|
||||
return;
|
||||
}
|
||||
|
||||
if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
|
||||
if (intent.hasExtra("state") && intent.getIntExtra("state", 0) == 0)
|
||||
pause();
|
||||
} else {
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
if (device.getBluetoothClass().hasService(BluetoothClass.Service.AUDIO))
|
||||
pause();
|
||||
}
|
||||
if (intent.getAction() == AudioManager.ACTION_AUDIO_BECOMING_NOISY)
|
||||
pause();
|
||||
}
|
||||
}, filter);
|
||||
|
||||
|
@@ -25,16 +25,18 @@ import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
public class Receiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d("Receiver", "onReceive: " + intent);
|
||||
if (intent.getAction() == "android.intent.action.BOOT_COMPLETED") {
|
||||
if (Settings.Preferences.getBoolean(context,
|
||||
Settings.Preferences.KEY_RUN_ON_BOOT, false)) {
|
||||
final boolean wakelock = Settings.Preferences.getBoolean(context,
|
||||
Settings.Preferences.KEY_WAKELOCK, false);
|
||||
Main.start(context, wakelock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d("Receiver", "onReceive: " + intent);
|
||||
if (intent.getAction() == "android.intent.action.BOOT_COMPLETED") {
|
||||
if (Settings.Preferences.getBoolean(context,
|
||||
Settings.Preferences.KEY_RUN_ON_BOOT,
|
||||
false)) {
|
||||
final boolean wakelock =
|
||||
Settings.Preferences.getBoolean(context,
|
||||
Settings.Preferences.KEY_WAKELOCK, false);
|
||||
Main.start(context, wakelock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -38,7 +38,10 @@ author = 'Max Kellermann'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.23.5'
|
||||
with open('../meson.build') as f:
|
||||
import re
|
||||
version = re.match(r"project\([^\)]*\bversion:\s*'([^']+)'",
|
||||
f.read(4096)).group(1)
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
#release = version + '~git'
|
||||
|
||||
@@ -47,7 +50,7 @@ version = '0.23.5'
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
language = "en"
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
|
@@ -11,6 +11,12 @@ Music Player Daemon
|
||||
client
|
||||
protocol
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: man pages:
|
||||
|
||||
mpd.1
|
||||
mpd.conf.5
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
@@ -79,7 +79,7 @@
|
||||
# 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.
|
||||
# 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
|
||||
#bind_to_address "any"
|
||||
@@ -185,7 +185,7 @@
|
||||
# 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"
|
||||
#database {
|
||||
|
@@ -206,6 +206,11 @@ Plays audio CDs using libcdio. The URI has the form: "cdda://[DEVICE][/TRACK]".
|
||||
- If the CD drive does not specify a byte order, MPD assumes it is the CPU's native byte order. This setting allows overriding this.
|
||||
* - **speed N**
|
||||
- Request CDParanoia cap the extraction speed to Nx normal CD audio rotation speed, keeping the drive quiet.
|
||||
* - **mode disable|overlap|full**
|
||||
- Set the paranoia mode; ``disable`` means no fixups, ``overlap``
|
||||
performs overlapped reads, and ``full`` enables all options.
|
||||
* - **skip yes|no**
|
||||
- If set to ``no``, then never skip failed reads.
|
||||
|
||||
curl
|
||||
----
|
||||
@@ -214,8 +219,9 @@ Opens remote files or streams over HTTP using libcurl.
|
||||
|
||||
Note that unless overridden by the below settings (e.g. by setting
|
||||
them to a blank value), general curl configuration from environment
|
||||
variables such as ``http_proxy`` or specified in :file:`~/.curlrc`
|
||||
will be in effect.
|
||||
variables such as ``http_proxy`` will be in effect.
|
||||
|
||||
User name and password are read from an optional :file:`~/.netrc`, :file:`~/.curlrc` is not read.
|
||||
|
||||
.. list-table::
|
||||
:widths: 20 80
|
||||
|
@@ -479,7 +479,7 @@ Querying :program:`MPD`'s status
|
||||
current song in seconds, but with higher resolution.
|
||||
- ``duration`` [#since_0_20]_: Duration of the current song in seconds.
|
||||
- ``bitrate``: instantaneous bitrate in kbps
|
||||
- ``xfade``: ``crossfade`` in seconds
|
||||
- ``xfade``: ``crossfade`` in seconds (see :ref:`crossfading`)
|
||||
- ``mixrampdb``: ``mixramp`` threshold in dB
|
||||
- ``mixrampdelay``: ``mixrampdelay`` in seconds
|
||||
- ``audio``: The format emitted by the decoder plugin during
|
||||
@@ -519,17 +519,19 @@ Playback options
|
||||
.. _command_crossfade:
|
||||
|
||||
:command:`crossfade {SECONDS}`
|
||||
Sets crossfading between songs.
|
||||
Sets crossfading between songs. See :ref:`crossfading`.
|
||||
|
||||
.. _command_mixrampdb:
|
||||
|
||||
: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 {SECONDS}`
|
||||
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:
|
||||
|
||||
@@ -543,6 +545,13 @@ Playback options
|
||||
Sets repeat state to ``STATE``,
|
||||
``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 {VOL}`
|
||||
@@ -771,8 +780,8 @@ Whenever possible, ids should be used.
|
||||
.. _command_playlistfind:
|
||||
|
||||
:command:`playlistfind {FILTER}`
|
||||
Finds songs in the queue with strict
|
||||
matching.
|
||||
Search the queue for songs matching
|
||||
``FILTER`` (see :ref:`Filters <filter_syntax>`).
|
||||
|
||||
.. _command_playlistid:
|
||||
|
||||
@@ -792,8 +801,10 @@ Whenever possible, ids should be used.
|
||||
.. _command_playlistsearch:
|
||||
|
||||
:command:`playlistsearch {FILTER}`
|
||||
Searches case-insensitively for partial matches in the
|
||||
queue.
|
||||
Search the queue for songs matching
|
||||
``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:
|
||||
|
||||
|
78
doc/user.rst
78
doc/user.rst
@@ -36,7 +36,9 @@ Installing on Android
|
||||
|
||||
An experimental Android build is available on Google Play. After installing and launching it, :program:`MPD` will scan the music in your Music directory and you can control it as usual with a :program:`MPD` client.
|
||||
|
||||
If you need to tweak the configuration, you can create a file called :file:`mpd.conf` on the data partition (the directory which is returned by Android's :dfn:`getExternalStorageDirectory()` API function).
|
||||
If you need to tweak the configuration, you can create a file called
|
||||
:file:`mpd.conf` in MPD's data directory on the external storage
|
||||
(usually :file:`Android/data/org.musicpd/files/mpd.conf`).
|
||||
|
||||
ALSA is not available on Android; only the :ref:`OpenSL ES
|
||||
<sles_output>` output plugin can be used for local playback.
|
||||
@@ -197,7 +199,7 @@ Compiling for Android
|
||||
You need:
|
||||
|
||||
* Android SDK
|
||||
* `Android NDK r23 <https://developer.android.com/ndk/downloads>`_
|
||||
* `Android NDK r25b <https://developer.android.com/ndk/downloads>`_
|
||||
* `Meson 0.56.0 <http://mesonbuild.com/>`__ and `Ninja
|
||||
<https://ninja-build.org/>`__
|
||||
* cmake
|
||||
@@ -301,7 +303,7 @@ Configuring neighbor plugins
|
||||
----------------------------
|
||||
|
||||
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`:
|
||||
|
||||
.. code-block:: none
|
||||
@@ -538,7 +540,7 @@ The following table lists the playlist_plugin options valid for all plugins:
|
||||
|
||||
* - Name
|
||||
- Description
|
||||
* - **plugin**
|
||||
* - **name**
|
||||
- The name of the plugin
|
||||
* - **enabled yes|no**
|
||||
- Allows you to disable a playlist plugin without recompiling. By default, all plugins are enabled.
|
||||
@@ -622,6 +624,51 @@ enabled by setting ``volume_normalization`` to ``yes``. It supports
|
||||
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
|
||||
------------------
|
||||
|
||||
@@ -1018,7 +1065,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.
|
||||
|
||||
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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -1058,6 +1117,15 @@ See :ref:`tags` for a list of supported tags.
|
||||
The :ref:`metadata_to_use <metadata_to_use>` setting can be used to
|
||||
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
|
||||
---------
|
||||
|
||||
|
16
meson.build
16
meson.build
@@ -1,7 +1,7 @@
|
||||
project(
|
||||
'mpd',
|
||||
['c', 'cpp'],
|
||||
version: '0.23.5',
|
||||
version: '0.23.11',
|
||||
meson_version: '>= 0.56.0',
|
||||
default_options: [
|
||||
'c_std=c11',
|
||||
@@ -73,6 +73,9 @@ test_common_flags = [
|
||||
# clang specific warning options:
|
||||
'-Wunreachable-code-aggressive',
|
||||
'-Wused-but-marked-unused',
|
||||
|
||||
# suppress bogus GCC12 warnings in libfmt headers
|
||||
'-Wno-stringop-overflow',
|
||||
]
|
||||
|
||||
test_global_cxxflags = test_global_common_flags + [
|
||||
@@ -248,6 +251,14 @@ endif
|
||||
|
||||
fmt_dep = dependency('fmt', fallback: ['fmt', 'fmt_dep'])
|
||||
|
||||
if compiler.get_id() == 'clang' and compiler.version().version_compare('<15')
|
||||
fmt_dep = declare_dependency(
|
||||
dependencies: fmt_dep,
|
||||
# suppress bogus clang 14 warning (the version in Android NDK r25b)
|
||||
compile_args: ['-Wno-unused-local-typedef'],
|
||||
)
|
||||
endif
|
||||
|
||||
log = static_library(
|
||||
'log',
|
||||
'src/Log.cxx',
|
||||
@@ -349,7 +360,7 @@ sources = [
|
||||
'src/TagStream.cxx',
|
||||
'src/TagAny.cxx',
|
||||
'src/TimePrint.cxx',
|
||||
'src/mixer/Volume.cxx',
|
||||
'src/mixer/Memento.cxx',
|
||||
'src/PlaylistFile.cxx',
|
||||
]
|
||||
|
||||
@@ -379,6 +390,7 @@ endif
|
||||
|
||||
if enable_database
|
||||
sources += [
|
||||
'src/storage/StorageState.cxx',
|
||||
'src/queue/PlaylistUpdate.cxx',
|
||||
'src/command/StorageCommands.cxx',
|
||||
'src/command/DatabaseCommands.cxx',
|
||||
|
@@ -12,14 +12,14 @@ from build.boost import BoostProject
|
||||
from build.jack import JackProject
|
||||
|
||||
libmpdclient = MesonProject(
|
||||
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.19.tar.xz',
|
||||
'158aad4c2278ab08e76a3f2b0166c99b39fae00ee17231bd225c5a36e977a189',
|
||||
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.20.tar.xz',
|
||||
'18793f68e939c3301e34d8fcadea1f7daa24143941263cecadb80126194e277d',
|
||||
'lib/libmpdclient.a',
|
||||
)
|
||||
|
||||
libogg = CmakeProject(
|
||||
'http://downloads.xiph.org/releases/ogg/libogg-1.3.4.tar.xz',
|
||||
'c163bc12bc300c401b6aa35907ac682671ea376f13ae0969a220f7ddf71893fe',
|
||||
'http://downloads.xiph.org/releases/ogg/libogg-1.3.5.tar.xz',
|
||||
'c4d91be36fc8e54deae7575241e03f4211eb102afb3fc0775fbbc1b740016705',
|
||||
'lib/libogg.a',
|
||||
[
|
||||
'-DBUILD_SHARED_LIBS=OFF',
|
||||
@@ -43,20 +43,22 @@ opus = AutotoolsProject(
|
||||
)
|
||||
|
||||
flac = AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/flac/flac-1.3.3.tar.xz',
|
||||
'213e82bd716c9de6db2f98bcadbc4c24c7e2efe8c75939a1a84e28539c4e1748',
|
||||
'http://downloads.xiph.org/releases/flac/flac-1.4.2.tar.xz',
|
||||
'e322d58a1f48d23d9dd38f432672865f6f79e73a6f9cc5a5f57fcaa83eb5a8e4',
|
||||
'lib/libFLAC.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--disable-stack-smash-protection',
|
||||
'--disable-xmms-plugin', '--disable-cpplibs',
|
||||
'--disable-doxygen-docs',
|
||||
'--disable-programs',
|
||||
],
|
||||
subdirs=['include', 'src/libFLAC'],
|
||||
)
|
||||
|
||||
zlib = ZlibProject(
|
||||
'http://zlib.net/zlib-1.2.11.tar.xz',
|
||||
'4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066',
|
||||
'http://zlib.net/zlib-1.2.13.tar.xz',
|
||||
'd14c38e313afc35a9a8760dadf26042f51ea0f5d154b0630a31da0540107fb98',
|
||||
'lib/libz.a',
|
||||
)
|
||||
|
||||
@@ -112,16 +114,20 @@ libmodplug = AutotoolsProject(
|
||||
)
|
||||
|
||||
libopenmpt = AutotoolsProject(
|
||||
'https://lib.openmpt.org/files/libopenmpt/src/libopenmpt-0.5.12+release.autotools.tar.gz',
|
||||
'892aea7a599b5d21842bebf463b5aafdad5711be7008dd84401920c6234820af',
|
||||
'https://lib.openmpt.org/files/libopenmpt/src/libopenmpt-0.6.6+release.autotools.tar.gz',
|
||||
'6ddb9e26a430620944891796fefb1bbb38bd9148f6cfc558810c0d3f269876c7',
|
||||
'lib/libopenmpt.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--disable-openmpt123',
|
||||
'--disable-examples',
|
||||
'--disable-tests',
|
||||
'--disable-doxygen-doc',
|
||||
'--without-mpg123', '--without-ogg', '--without-vorbis', '--without-vorbisfile',
|
||||
'--without-portaudio', '--without-portaudiocpp', '--without-sndfile',
|
||||
'--without-flac',
|
||||
],
|
||||
base='libopenmpt-0.5.12+release.autotools',
|
||||
base='libopenmpt-0.6.6+release.autotools',
|
||||
)
|
||||
|
||||
wildmidi = CmakeProject(
|
||||
@@ -151,8 +157,8 @@ gme = CmakeProject(
|
||||
)
|
||||
|
||||
ffmpeg = FfmpegProject(
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.4.1.tar.xz',
|
||||
'eadbad9e9ab30b25f5520fbfde99fae4a92a1ae3c0257a8d68569a4651e30e02',
|
||||
'http://ffmpeg.org/releases/ffmpeg-5.1.2.tar.xz',
|
||||
'619e706d662c8420859832ddc259cd4d4096a48a2ce1eefd052db9e440eef3dc',
|
||||
'lib/libavcodec.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -166,7 +172,6 @@ ffmpeg = FfmpegProject(
|
||||
'--disable-swscale',
|
||||
'--disable-postproc',
|
||||
'--disable-avfilter',
|
||||
'--disable-lzo',
|
||||
'--disable-faan',
|
||||
'--disable-pixelutils',
|
||||
'--disable-network',
|
||||
@@ -177,6 +182,8 @@ ffmpeg = FfmpegProject(
|
||||
'--disable-filters',
|
||||
'--disable-v4l2_m2m',
|
||||
|
||||
'--disable-vulkan',
|
||||
|
||||
'--disable-parser=bmp',
|
||||
'--disable-parser=cavsvideo',
|
||||
'--disable-parser=dvbsub',
|
||||
@@ -380,19 +387,18 @@ ffmpeg = FfmpegProject(
|
||||
)
|
||||
|
||||
openssl = OpenSSLProject(
|
||||
'https://www.openssl.org/source/openssl-3.0.0.tar.gz',
|
||||
'59eedfcb46c25214c9bd37ed6078297b4df01d012267fe9e9eee31f61bc70536',
|
||||
'https://www.openssl.org/source/openssl-3.0.7.tar.gz',
|
||||
'83049d042a260e696f62406ac5c08bf706fd84383f945cf21bd61e9ed95c396e',
|
||||
'include/openssl/ossl_typ.h',
|
||||
)
|
||||
|
||||
curl = CmakeProject(
|
||||
'https://curl.se/download/curl-7.79.1.tar.xz',
|
||||
'0606f74b1182ab732a17c11613cbbaf7084f2e6cca432642d0e3ad7c224c3689',
|
||||
'https://curl.se/download/curl-7.86.0.tar.xz',
|
||||
'2d61116e5f485581f6d59865377df4463f2e788677ac43222b496d4e49fb627b',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'-DBUILD_CURL_EXE=OFF',
|
||||
'-DBUILD_SHARED_LIBS=OFF',
|
||||
'-DCURL_DISABLE_VERBOSE_STRINGS=ON',
|
||||
'-DCURL_DISABLE_LDAP=ON',
|
||||
'-DCURL_DISABLE_TELNET=ON',
|
||||
'-DCURL_DISABLE_DICT=ON',
|
||||
@@ -415,14 +421,14 @@ curl = CmakeProject(
|
||||
'-DBUILD_TESTING=OFF',
|
||||
],
|
||||
windows_configure_args=[
|
||||
'-DCMAKE_USE_SCHANNEL=ON',
|
||||
'-DCURL_USE_SCHANNEL=ON',
|
||||
],
|
||||
patches='src/lib/curl/patches',
|
||||
)
|
||||
|
||||
libnfs = AutotoolsProject(
|
||||
'https://github.com/sahlberg/libnfs/archive/libnfs-4.0.0.tar.gz',
|
||||
'6ee77e9fe220e2d3e3b1f53cfea04fb319828cc7dbb97dd9df09e46e901d797d',
|
||||
'https://github.com/sahlberg/libnfs/archive/libnfs-5.0.2.tar.gz',
|
||||
'637e56643b19da9fba98f06847788c4dad308b723156a64748041035dcdf9bd3',
|
||||
'lib/libnfs.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -433,8 +439,7 @@ libnfs = AutotoolsProject(
|
||||
|
||||
'--disable-utils', '--disable-examples',
|
||||
],
|
||||
base='libnfs-libnfs-4.0.0',
|
||||
patches='src/lib/nfs/patches',
|
||||
base='libnfs-libnfs-5.0.2',
|
||||
autoreconf=True,
|
||||
)
|
||||
|
||||
@@ -445,7 +450,7 @@ jack = JackProject(
|
||||
)
|
||||
|
||||
boost = BoostProject(
|
||||
'https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.bz2',
|
||||
'fc9f85fc030e233142908241af7a846e60630aa7388de9a5fafb1f3a26840854',
|
||||
'https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.bz2',
|
||||
'1e19565d82e43bc59209a168f5ac899d3ba471d55c7610c677d4ccf2c9c500c0',
|
||||
'include/boost/version.hpp',
|
||||
)
|
||||
|
@@ -53,19 +53,21 @@ pkgconfig = '{toolchain.pkg_config}'
|
||||
f.write(f"""
|
||||
[properties]
|
||||
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:
|
||||
f.write("""
|
||||
# Keep Meson from executing Android-x86 test binariees
|
||||
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"""
|
||||
@@ -80,8 +82,8 @@ endian = '{endian}'
|
||||
def configure(toolchain, src, build, args=()):
|
||||
cross_file = make_cross_file(toolchain)
|
||||
configure = [
|
||||
'meson',
|
||||
src, build,
|
||||
'meson', 'setup',
|
||||
build, src,
|
||||
|
||||
'--prefix', toolchain.install_prefix,
|
||||
|
||||
|
@@ -48,15 +48,14 @@ LocateFileUri(const char *uri, const Client *client
|
||||
/* this path was relative to the music
|
||||
directory */
|
||||
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
|
||||
return LocatedUri(LocatedUri::Type::RELATIVE,
|
||||
suffix.data());
|
||||
return {LocatedUri::Type::RELATIVE, suffix.data()};
|
||||
}
|
||||
#endif
|
||||
|
||||
if (client != nullptr)
|
||||
client->AllowFile(path);
|
||||
|
||||
return LocatedUri(LocatedUri::Type::PATH, uri, std::move(path));
|
||||
return {LocatedUri::Type::PATH, uri, std::move(path)};
|
||||
}
|
||||
|
||||
static LocatedUri
|
||||
@@ -90,8 +89,7 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
|
||||
const auto suffix = storage->MapToRelativeUTF8(uri);
|
||||
if (suffix.data() != nullptr)
|
||||
// TODO: don't use suffix.data() (ok for now because we know it's null-terminated)
|
||||
return LocatedUri(LocatedUri::Type::RELATIVE,
|
||||
suffix.data());
|
||||
return {LocatedUri::Type::RELATIVE, suffix.data()};
|
||||
}
|
||||
|
||||
if (kind == UriPluginKind::STORAGE &&
|
||||
@@ -99,7 +97,7 @@ LocateAbsoluteUri(UriPluginKind kind, const char *uri
|
||||
throw std::invalid_argument("Unsupported URI scheme");
|
||||
#endif
|
||||
|
||||
return LocatedUri(LocatedUri::Type::ABSOLUTE, uri);
|
||||
return {LocatedUri::Type::ABSOLUTE, uri};
|
||||
}
|
||||
|
||||
LocatedUri
|
||||
|
@@ -45,7 +45,10 @@ void
|
||||
LogFmt(LogLevel level, const Domain &domain,
|
||||
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),
|
||||
fmt::make_args_checked<Args...>(format_str,
|
||||
args...));
|
||||
|
@@ -158,12 +158,15 @@ log_init(const ConfigData &config, bool verbose, bool use_stdout)
|
||||
getenv("NOTIFY_SOCKET") != nullptr) {
|
||||
/* if MPD was started as a systemd
|
||||
service, default to journal (which
|
||||
is connected to fd=2) */
|
||||
is connected to stdout&stderr) */
|
||||
out_fd = STDOUT_FILENO;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifndef HAVE_SYSLOG
|
||||
#ifdef _WIN32
|
||||
/* default to stdout on Windows */
|
||||
out_fd = STDOUT_FILENO;
|
||||
#elif !defined(HAVE_SYSLOG)
|
||||
throw std::runtime_error("config parameter 'log_file' not found");
|
||||
#endif
|
||||
#ifdef HAVE_SYSLOG
|
||||
|
48
src/Main.cxx
48
src/Main.cxx
@@ -590,19 +590,46 @@ MainConfigured(const CommandLineOptions &options,
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
/**
|
||||
* Wrapper for ReadConfigFile() which returns false if the file was
|
||||
* not found.
|
||||
*/
|
||||
static bool
|
||||
TryReadConfigFile(ConfigData &config, Path path)
|
||||
{
|
||||
if (!FileExists(path))
|
||||
return false;
|
||||
|
||||
ReadConfigFile(config, path);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
AndroidMain()
|
||||
LoadConfigFile(JNIEnv *env, ConfigData &config)
|
||||
{
|
||||
/* try loading mpd.conf from
|
||||
"Android/data/org.musicpd/files/mpd.conf" (the app specific
|
||||
data directory) first */
|
||||
if (const auto dir = context->GetExternalFilesDir(env);
|
||||
!dir.IsNull() &&
|
||||
TryReadConfigFile(config, dir / Path::FromFS("mpd.conf")))
|
||||
return;
|
||||
|
||||
/* if that fails, attempt to load "mpd.conf" from the root of
|
||||
the SD card (pre-0.23.9, ceases to work since Android
|
||||
12) */
|
||||
if (const auto dir = Environment::getExternalStorageDirectory(env);
|
||||
!dir.IsNull())
|
||||
TryReadConfigFile(config, dir / Path::FromFS("mpd.conf"));
|
||||
}
|
||||
|
||||
static void
|
||||
AndroidMain(JNIEnv *env)
|
||||
{
|
||||
CommandLineOptions options;
|
||||
ConfigData raw_config;
|
||||
|
||||
const auto sdcard = Environment::getExternalStorageDirectory();
|
||||
if (!sdcard.IsNull()) {
|
||||
const auto config_path =
|
||||
sdcard / Path::FromFS("mpd.conf");
|
||||
if (FileExists(config_path))
|
||||
ReadConfigFile(raw_config, config_path);
|
||||
}
|
||||
LoadConfigFile(env, raw_config);
|
||||
|
||||
MainConfigured(options, raw_config);
|
||||
}
|
||||
@@ -614,9 +641,12 @@ Java_org_musicpd_Bridge_run(JNIEnv *env, jclass, jobject _context, jobject _logL
|
||||
Java::Init(env);
|
||||
Java::Object::Initialise(env);
|
||||
Java::File::Initialise(env);
|
||||
|
||||
Environment::Initialise(env);
|
||||
AtScopeExit(env) { Environment::Deinitialise(env); };
|
||||
|
||||
Context::Initialise(env);
|
||||
|
||||
context = new Context(env, _context);
|
||||
AtScopeExit() { delete context; };
|
||||
|
||||
@@ -625,7 +655,7 @@ Java_org_musicpd_Bridge_run(JNIEnv *env, jclass, jobject _context, jobject _logL
|
||||
AtScopeExit() { delete logListener; };
|
||||
|
||||
try {
|
||||
AndroidMain();
|
||||
AndroidMain(env);
|
||||
} catch (...) {
|
||||
LogError(std::current_exception());
|
||||
}
|
||||
|
@@ -23,7 +23,6 @@
|
||||
#include "Log.hxx"
|
||||
#include "lib/fmt/ExceptionFormatter.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
#include "mixer/Volume.hxx"
|
||||
#include "IdleFlags.hxx"
|
||||
#include "client/Listener.hxx"
|
||||
#include "client/Client.hxx"
|
||||
@@ -206,7 +205,7 @@ Partition::OnBorderPause() noexcept
|
||||
void
|
||||
Partition::OnMixerVolumeChanged(Mixer &, int) noexcept
|
||||
{
|
||||
InvalidateHardwareVolume();
|
||||
mixer_memento.InvalidateHardwareVolume();
|
||||
|
||||
/* notify clients */
|
||||
EmitIdle(IDLE_MIXER);
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include "queue/Listener.hxx"
|
||||
#include "output/MultipleOutputs.hxx"
|
||||
#include "mixer/Listener.hxx"
|
||||
#include "mixer/Memento.hxx"
|
||||
#include "player/Control.hxx"
|
||||
#include "player/Listener.hxx"
|
||||
#include "protocol/RangeArg.hxx"
|
||||
@@ -76,6 +77,8 @@ struct Partition final : QueueListener, PlayerListener, MixerListener {
|
||||
|
||||
MultipleOutputs outputs;
|
||||
|
||||
MixerMemento mixer_memento;
|
||||
|
||||
PlayerControl pc;
|
||||
|
||||
ReplayGainMode replay_gain_mode = ReplayGainMode::OFF;
|
||||
|
@@ -19,8 +19,8 @@
|
||||
|
||||
#include "PlaylistDatabase.hxx"
|
||||
#include "db/PlaylistVector.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "io/LineReader.hxx"
|
||||
#include "io/BufferedOutputStream.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/StringStrip.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
@@ -42,7 +42,7 @@ playlist_vector_save(BufferedOutputStream &os, const PlaylistVector &pv)
|
||||
}
|
||||
|
||||
void
|
||||
playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name)
|
||||
playlist_metadata_load(LineReader &file, PlaylistVector &pv, const char *name)
|
||||
{
|
||||
PlaylistInfo pm(name);
|
||||
|
||||
|
@@ -24,7 +24,7 @@
|
||||
|
||||
class PlaylistVector;
|
||||
class BufferedOutputStream;
|
||||
class TextFile;
|
||||
class LineReader;
|
||||
|
||||
void
|
||||
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.
|
||||
*/
|
||||
void
|
||||
playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name);
|
||||
playlist_metadata_load(LineReader &file, PlaylistVector &pv,
|
||||
const char *name);
|
||||
|
||||
#endif
|
||||
|
@@ -28,8 +28,8 @@
|
||||
#include "Mapper.hxx"
|
||||
#include "protocol/RangeArg.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "fs/io/FileOutputStream.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "io/FileOutputStream.hxx"
|
||||
#include "io/BufferedOutputStream.hxx"
|
||||
#include "config/Data.hxx"
|
||||
#include "config/Option.hxx"
|
||||
#include "config/Defaults.hxx"
|
||||
@@ -81,6 +81,9 @@ spl_valid_name(const char *name_utf8)
|
||||
*/
|
||||
|
||||
return std::strchr(name_utf8, '/') == nullptr &&
|
||||
#ifdef _WIN32
|
||||
std::strchr(name_utf8, '\\') == nullptr &&
|
||||
#endif
|
||||
std::strchr(name_utf8, '\n') == nullptr &&
|
||||
std::strchr(name_utf8, '\r') == nullptr;
|
||||
}
|
||||
|
@@ -28,8 +28,8 @@
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/io/FileOutputStream.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "io/FileOutputStream.hxx"
|
||||
#include "io/BufferedOutputStream.hxx"
|
||||
#include "util/UriExtract.hxx"
|
||||
|
||||
static void
|
||||
|
@@ -31,7 +31,7 @@ enum class SingleMode : uint8_t {
|
||||
/**
|
||||
* Return the string representation of a #SingleMode.
|
||||
*/
|
||||
[[gnu::pure]]
|
||||
[[gnu::const]]
|
||||
const char *
|
||||
SingleToString(SingleMode mode) noexcept;
|
||||
|
||||
|
@@ -22,8 +22,8 @@
|
||||
#include "db/plugins/simple/Song.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
#include "TagSave.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "io/LineReader.hxx"
|
||||
#include "io/BufferedOutputStream.hxx"
|
||||
#include "tag/ParseName.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "tag/Builder.hxx"
|
||||
@@ -85,7 +85,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song)
|
||||
}
|
||||
|
||||
DetachedSong
|
||||
song_load(TextFile &file, const char *uri,
|
||||
song_load(LineReader &file, const char *uri,
|
||||
std::string *target_r)
|
||||
{
|
||||
DetachedSong song(uri);
|
||||
|
@@ -28,7 +28,7 @@ struct Song;
|
||||
struct AudioFormat;
|
||||
class DetachedSong;
|
||||
class BufferedOutputStream;
|
||||
class TextFile;
|
||||
class LineReader;
|
||||
|
||||
void
|
||||
song_save(BufferedOutputStream &os, const Song &song);
|
||||
@@ -43,7 +43,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song);
|
||||
* Throws on error.
|
||||
*/
|
||||
DetachedSong
|
||||
song_load(TextFile &file, const char *uri,
|
||||
song_load(LineReader &file, const char *uri,
|
||||
std::string *target_r=nullptr);
|
||||
|
||||
#endif
|
||||
|
@@ -22,12 +22,11 @@
|
||||
#include "output/State.hxx"
|
||||
#include "queue/PlaylistState.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "fs/io/FileOutputStream.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "io/FileOutputStream.hxx"
|
||||
#include "io/BufferedOutputStream.hxx"
|
||||
#include "storage/StorageState.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "mixer/Volume.hxx"
|
||||
#include "SongLoader.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
@@ -47,7 +46,7 @@ StateFile::StateFile(StateFileConfig &&_config,
|
||||
void
|
||||
StateFile::RememberVersions() noexcept
|
||||
{
|
||||
prev_volume_version = sw_volume_state_get_hash();
|
||||
prev_volume_version = partition.mixer_memento.GetSoftwareVolumeStateHash();
|
||||
prev_output_version = audio_output_state_get_version();
|
||||
prev_playlist_version = playlist_state_get_hash(partition.playlist,
|
||||
partition.pc);
|
||||
@@ -59,7 +58,7 @@ StateFile::RememberVersions() noexcept
|
||||
bool
|
||||
StateFile::IsModified() const noexcept
|
||||
{
|
||||
return prev_volume_version != sw_volume_state_get_hash() ||
|
||||
return prev_volume_version != partition.mixer_memento.GetSoftwareVolumeStateHash() ||
|
||||
prev_output_version != audio_output_state_get_version() ||
|
||||
prev_playlist_version != playlist_state_get_hash(partition.playlist,
|
||||
partition.pc)
|
||||
@@ -72,7 +71,7 @@ StateFile::IsModified() const noexcept
|
||||
inline void
|
||||
StateFile::Write(BufferedOutputStream &os)
|
||||
{
|
||||
save_sw_volume_state(os);
|
||||
partition.mixer_memento.SaveSoftwareVolumeState(os);
|
||||
audio_output_state_save(os, partition.outputs);
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
@@ -125,7 +124,7 @@ try {
|
||||
|
||||
const char *line;
|
||||
while ((line = file.ReadLine()) != nullptr) {
|
||||
success = read_sw_volume_state(line, partition.outputs) ||
|
||||
success = partition.mixer_memento.LoadSoftwareVolumeState(line, partition.outputs) ||
|
||||
audio_output_state_read(line, partition.outputs) ||
|
||||
playlist_state_restore(config, line, file, song_loader,
|
||||
partition.playlist,
|
||||
|
@@ -32,7 +32,7 @@ StateFileConfig::StateFileConfig(const ConfigData &config)
|
||||
{
|
||||
#ifdef ANDROID
|
||||
if (path.IsNull()) {
|
||||
const auto cache_dir = GetUserCacheDir();
|
||||
const auto cache_dir = GetAppCacheDir();
|
||||
if (cache_dir.IsNull())
|
||||
return;
|
||||
|
||||
|
@@ -21,12 +21,16 @@
|
||||
#include "TagStream.hxx"
|
||||
#include "TagFile.hxx"
|
||||
#include "tag/Generic.hxx"
|
||||
#include "song/LightSong.hxx"
|
||||
#include "db/Interface.hxx"
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#include "client/Client.hxx"
|
||||
#include "protocol/Ack.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "util/Compiler.h"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/UriExtract.hxx"
|
||||
#include "LocateUri.hxx"
|
||||
|
||||
@@ -51,10 +55,67 @@ TagScanFile(const Path path_fs, TagHandler &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
|
||||
TagScanDatabase(Client &client, const char *uri, TagHandler &handler)
|
||||
{
|
||||
#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();
|
||||
if (storage == nullptr) {
|
||||
#else
|
||||
|
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "TagSave.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "io/BufferedOutputStream.hxx"
|
||||
|
||||
#define SONG_TIME "Time: "
|
||||
|
||||
|
@@ -26,19 +26,30 @@
|
||||
|
||||
#include "AudioManager.hxx"
|
||||
|
||||
AllocatedPath
|
||||
Context::GetExternalFilesDir(JNIEnv *env, const char *_type) noexcept
|
||||
static jmethodID getExternalFilesDir_method,
|
||||
getCacheDir_method,
|
||||
getSystemService_method;
|
||||
|
||||
void
|
||||
Context::Initialise(JNIEnv *env) noexcept
|
||||
{
|
||||
assert(_type != nullptr);
|
||||
Java::Class cls{env, "android/content/Context"};
|
||||
|
||||
Java::Class cls{env, env->GetObjectClass(Get())};
|
||||
jmethodID method = env->GetMethodID(cls, "getExternalFilesDir",
|
||||
"(Ljava/lang/String;)Ljava/io/File;");
|
||||
assert(method);
|
||||
getExternalFilesDir_method = env->GetMethodID(cls, "getExternalFilesDir",
|
||||
"(Ljava/lang/String;)Ljava/io/File;");
|
||||
getCacheDir_method = env->GetMethodID(cls, "getCacheDir",
|
||||
"()Ljava/io/File;");
|
||||
getSystemService_method = env->GetMethodID(cls, "getSystemService",
|
||||
"(Ljava/lang/String;)Ljava/lang/Object;");
|
||||
}
|
||||
|
||||
Java::String type{env, _type};
|
||||
AllocatedPath
|
||||
Context::GetExternalFilesDir(JNIEnv *env, const char *type) noexcept
|
||||
{
|
||||
assert(type != nullptr);
|
||||
|
||||
jobject file = env->CallObjectMethod(Get(), method, type.Get());
|
||||
jobject file = env->CallObjectMethod(Get(), getExternalFilesDir_method,
|
||||
Java::String::Optional(env, type).Get());
|
||||
if (Java::DiscardException(env) || file == nullptr)
|
||||
return nullptr;
|
||||
|
||||
@@ -50,12 +61,7 @@ Context::GetCacheDir(JNIEnv *env) const noexcept
|
||||
{
|
||||
assert(env != nullptr);
|
||||
|
||||
Java::Class cls(env, env->GetObjectClass(Get()));
|
||||
jmethodID method = env->GetMethodID(cls, "getCacheDir",
|
||||
"()Ljava/io/File;");
|
||||
assert(method);
|
||||
|
||||
jobject file = env->CallObjectMethod(Get(), method);
|
||||
jobject file = env->CallObjectMethod(Get(), getCacheDir_method);
|
||||
if (Java::DiscardException(env) || file == nullptr)
|
||||
return nullptr;
|
||||
|
||||
@@ -67,13 +73,8 @@ Context::GetAudioManager(JNIEnv *env) noexcept
|
||||
{
|
||||
assert(env != nullptr);
|
||||
|
||||
Java::Class cls(env, env->GetObjectClass(Get()));
|
||||
jmethodID method = env->GetMethodID(cls, "getSystemService",
|
||||
"(Ljava/lang/String;)Ljava/lang/Object;");
|
||||
assert(method);
|
||||
|
||||
Java::String name(env, "audio");
|
||||
jobject am = env->CallObjectMethod(Get(), method, name.Get());
|
||||
jobject am = env->CallObjectMethod(Get(), getSystemService_method, name.Get());
|
||||
if (Java::DiscardException(env) || am == nullptr)
|
||||
return nullptr;
|
||||
|
||||
|
@@ -27,12 +27,21 @@ class AudioManager;
|
||||
|
||||
class Context : public Java::GlobalObject {
|
||||
public:
|
||||
/**
|
||||
* Global initialisation. Looks up the methods of the
|
||||
* Context Java class.
|
||||
*/
|
||||
static void Initialise(JNIEnv *env) noexcept;
|
||||
|
||||
Context(JNIEnv *env, jobject obj) noexcept
|
||||
:Java::GlobalObject(env, obj) {}
|
||||
|
||||
/**
|
||||
* @param type the subdirectory name; may be nullptr
|
||||
*/
|
||||
[[gnu::pure]]
|
||||
AllocatedPath GetExternalFilesDir(JNIEnv *env,
|
||||
const char *type) noexcept;
|
||||
const char *type=nullptr) noexcept;
|
||||
|
||||
[[gnu::pure]]
|
||||
AllocatedPath GetCacheDir(JNIEnv *env) const noexcept;
|
||||
|
@@ -25,13 +25,13 @@
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
|
||||
namespace Environment {
|
||||
static Java::TrivialClass cls;
|
||||
static jmethodID getExternalStorageDirectory_method;
|
||||
static jmethodID getExternalStoragePublicDirectory_method;
|
||||
}
|
||||
|
||||
static Java::TrivialClass cls;
|
||||
static jmethodID getExternalStorageDirectory_method;
|
||||
static jmethodID getExternalStoragePublicDirectory_method;
|
||||
|
||||
void
|
||||
Environment::Initialise(JNIEnv *env) noexcept
|
||||
Initialise(JNIEnv *env) noexcept
|
||||
{
|
||||
cls.Find(env, "android/os/Environment");
|
||||
|
||||
@@ -45,16 +45,14 @@ Environment::Initialise(JNIEnv *env) noexcept
|
||||
}
|
||||
|
||||
void
|
||||
Environment::Deinitialise(JNIEnv *env) noexcept
|
||||
Deinitialise(JNIEnv *env) noexcept
|
||||
{
|
||||
cls.Clear(env);
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
Environment::getExternalStorageDirectory() noexcept
|
||||
getExternalStorageDirectory(JNIEnv *env) noexcept
|
||||
{
|
||||
JNIEnv *env = Java::GetEnv();
|
||||
|
||||
jobject file =
|
||||
env->CallStaticObjectMethod(cls,
|
||||
getExternalStorageDirectory_method);
|
||||
@@ -65,20 +63,20 @@ Environment::getExternalStorageDirectory() noexcept
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
Environment::getExternalStoragePublicDirectory(const char *type) noexcept
|
||||
getExternalStoragePublicDirectory(JNIEnv *env, const char *type) noexcept
|
||||
{
|
||||
if (getExternalStoragePublicDirectory_method == nullptr)
|
||||
/* needs API level 8 */
|
||||
return nullptr;
|
||||
|
||||
JNIEnv *env = Java::GetEnv();
|
||||
|
||||
Java::String type2(env, type);
|
||||
jobject file = env->CallStaticObjectMethod(Environment::cls,
|
||||
Environment::getExternalStoragePublicDirectory_method,
|
||||
jobject file = env->CallStaticObjectMethod(cls,
|
||||
getExternalStoragePublicDirectory_method,
|
||||
type2.Get());
|
||||
if (file == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return Java::File::ToAbsolutePath(env, file);
|
||||
}
|
||||
|
||||
} // namespace Environment
|
||||
|
@@ -17,27 +17,29 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_ANDROID_ENVIRONMENT_HXX
|
||||
#define MPD_ANDROID_ENVIRONMENT_HXX
|
||||
|
||||
#include "util/Compiler.h"
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
class AllocatedPath;
|
||||
|
||||
namespace Environment {
|
||||
void Initialise(JNIEnv *env) noexcept;
|
||||
void Deinitialise(JNIEnv *env) noexcept;
|
||||
|
||||
/**
|
||||
* Determine the mount point of the external SD card.
|
||||
*/
|
||||
[[gnu::pure]]
|
||||
AllocatedPath getExternalStorageDirectory() noexcept;
|
||||
void
|
||||
Initialise(JNIEnv *env) noexcept;
|
||||
|
||||
[[gnu::pure]]
|
||||
AllocatedPath getExternalStoragePublicDirectory(const char *type) noexcept;
|
||||
}
|
||||
void
|
||||
Deinitialise(JNIEnv *env) noexcept;
|
||||
|
||||
#endif
|
||||
/**
|
||||
* Determine the mount point of the external SD card.
|
||||
*/
|
||||
[[gnu::pure]]
|
||||
AllocatedPath
|
||||
getExternalStorageDirectory(JNIEnv *env) noexcept;
|
||||
|
||||
[[gnu::pure]]
|
||||
AllocatedPath
|
||||
getExternalStoragePublicDirectory(JNIEnv *env, const char *type) noexcept;
|
||||
|
||||
} // namespace Environment
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#include "ErrorRef.hxx"
|
||||
#include "StringRef.hxx"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Apple {
|
||||
@@ -57,8 +58,8 @@ ThrowOSStatus(OSStatus status, const char *_msg)
|
||||
const Apple::StringRef cfstr(cferr.CopyDescription());
|
||||
|
||||
char msg[1024];
|
||||
strcpy(msg, _msg);
|
||||
size_t length = strlen(msg);
|
||||
std::strcpy(msg, _msg);
|
||||
size_t length = std::strlen(msg);
|
||||
|
||||
cfstr.GetCString(msg + length, sizeof(msg) - length);
|
||||
throw std::runtime_error(msg);
|
||||
|
@@ -166,7 +166,7 @@ class Iso9660InputStream final : public InputStream {
|
||||
assert(fill <= data.size());
|
||||
assert(position <= fill);
|
||||
|
||||
return {&data[position], &data[fill]};
|
||||
return {data.data() + position, data.data() + fill};
|
||||
}
|
||||
|
||||
void Consume(size_t nbytes) noexcept {
|
||||
|
@@ -22,6 +22,10 @@ if libzzip_dep.found()
|
||||
found_archive_plugin = true
|
||||
endif
|
||||
|
||||
if not found_archive_plugin
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
archive_plugins = static_library(
|
||||
'archive_plugins',
|
||||
archive_plugins_sources,
|
||||
|
@@ -82,7 +82,10 @@ public:
|
||||
|
||||
template<typename S, typename... Args>
|
||||
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),
|
||||
fmt::make_args_checked<Args...>(format_str,
|
||||
args...));
|
||||
@@ -109,7 +112,10 @@ public:
|
||||
template<typename S, typename... Args>
|
||||
void FmtError(enum ack code,
|
||||
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),
|
||||
fmt::make_args_checked<Args...>(format_str,
|
||||
args...));
|
||||
|
@@ -160,8 +160,7 @@ find_stream_art(std::string_view directory, Mutex &mutex)
|
||||
static constexpr auto art_names = std::array {
|
||||
"cover.png",
|
||||
"cover.jpg",
|
||||
"cover.tiff",
|
||||
"cover.bmp",
|
||||
"cover.webp",
|
||||
};
|
||||
|
||||
for(const auto name : art_names) {
|
||||
|
@@ -33,7 +33,6 @@
|
||||
#include "TimePrint.hxx"
|
||||
#include "decoder/DecoderPrint.hxx"
|
||||
#include "ls.hxx"
|
||||
#include "mixer/Volume.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/StringAPI.hxx"
|
||||
@@ -325,7 +324,7 @@ handle_getvol(Client &client, Request, Response &r)
|
||||
{
|
||||
auto &partition = client.GetPartition();
|
||||
|
||||
const auto volume = volume_level_get(partition.outputs);
|
||||
const auto volume = partition.mixer_memento.GetVolume(partition.outputs);
|
||||
if (volume >= 0)
|
||||
r.Fmt(FMT_STRING("volume: {}\n"), volume);
|
||||
|
||||
@@ -333,15 +332,13 @@ handle_getvol(Client &client, Request, Response &r)
|
||||
}
|
||||
|
||||
CommandResult
|
||||
handle_setvol(Client &client, Request args, Response &r)
|
||||
handle_setvol(Client &client, Request args, Response &)
|
||||
{
|
||||
unsigned level = args.ParseUnsigned(0, 100);
|
||||
|
||||
if (!volume_level_change(client.GetPartition().outputs, level)) {
|
||||
r.Error(ACK_ERROR_SYSTEM, "problems setting volume");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
|
||||
auto &partition = client.GetPartition();
|
||||
partition.mixer_memento.SetVolume(partition.outputs, level);
|
||||
partition.EmitIdle(IDLE_MIXER);
|
||||
return CommandResult::OK;
|
||||
}
|
||||
|
||||
@@ -350,9 +347,11 @@ handle_volume(Client &client, Request args, Response &r)
|
||||
{
|
||||
int relative = args.ParseInt(0, -100, 100);
|
||||
|
||||
auto &outputs = client.GetPartition().outputs;
|
||||
auto &partition = client.GetPartition();
|
||||
auto &outputs = partition.outputs;
|
||||
auto &mixer_memento = partition.mixer_memento;
|
||||
|
||||
const int old_volume = volume_level_get(outputs);
|
||||
const int old_volume = mixer_memento.GetVolume(outputs);
|
||||
if (old_volume < 0) {
|
||||
r.Error(ACK_ERROR_SYSTEM, "No mixer");
|
||||
return CommandResult::ERROR;
|
||||
@@ -364,10 +363,9 @@ handle_volume(Client &client, Request args, Response &r)
|
||||
else if (new_volume > 100)
|
||||
new_volume = 100;
|
||||
|
||||
if (new_volume != old_volume &&
|
||||
!volume_level_change(outputs, new_volume)) {
|
||||
r.Error(ACK_ERROR_SYSTEM, "problems setting volume");
|
||||
return CommandResult::ERROR;
|
||||
if (new_volume != old_volume) {
|
||||
mixer_memento.SetVolume(outputs, new_volume);
|
||||
partition.EmitIdle(IDLE_MIXER);
|
||||
}
|
||||
|
||||
return CommandResult::OK;
|
||||
|
@@ -33,7 +33,11 @@ handle_enableoutput(Client &client, Request args, Response &r)
|
||||
assert(args.size == 1);
|
||||
unsigned device = args.ParseUnsigned(0);
|
||||
|
||||
if (!audio_output_enable_index(client.GetPartition().outputs, device)) {
|
||||
auto &partition = client.GetPartition();
|
||||
|
||||
if (!audio_output_enable_index(partition.outputs,
|
||||
partition.mixer_memento,
|
||||
device)) {
|
||||
r.Error(ACK_ERROR_NO_EXIST, "No such audio output");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
@@ -47,7 +51,11 @@ handle_disableoutput(Client &client, Request args, Response &r)
|
||||
assert(args.size == 1);
|
||||
unsigned device = args.ParseUnsigned(0);
|
||||
|
||||
if (!audio_output_disable_index(client.GetPartition().outputs, device)) {
|
||||
auto &partition = client.GetPartition();
|
||||
|
||||
if (!audio_output_disable_index(partition.outputs,
|
||||
partition.mixer_memento,
|
||||
device)) {
|
||||
r.Error(ACK_ERROR_NO_EXIST, "No such audio output");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
@@ -61,7 +69,11 @@ handle_toggleoutput(Client &client, Request args, Response &r)
|
||||
assert(args.size == 1);
|
||||
unsigned device = args.ParseUnsigned(0);
|
||||
|
||||
if (!audio_output_toggle_index(client.GetPartition().outputs, device)) {
|
||||
auto &partition = client.GetPartition();
|
||||
|
||||
if (!audio_output_toggle_index(partition.outputs,
|
||||
partition.mixer_memento,
|
||||
device)) {
|
||||
r.Error(ACK_ERROR_NO_EXIST, "No such audio output");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
|
@@ -25,7 +25,6 @@
|
||||
#include "SingleMode.hxx"
|
||||
#include "client/Client.hxx"
|
||||
#include "client/Response.hxx"
|
||||
#include "mixer/Volume.hxx"
|
||||
#include "Partition.hxx"
|
||||
#include "Instance.hxx"
|
||||
#include "IdleFlags.hxx"
|
||||
@@ -131,7 +130,7 @@ handle_status(Client &client, [[maybe_unused]] Request args, Response &r)
|
||||
|
||||
const auto &playlist = partition.playlist;
|
||||
|
||||
const auto volume = volume_level_get(partition.outputs);
|
||||
const auto volume = partition.mixer_memento.GetVolume(partition.outputs);
|
||||
if (volume >= 0)
|
||||
r.Fmt(FMT_STRING("volume: {}\n"), volume);
|
||||
|
||||
|
@@ -31,8 +31,8 @@
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/List.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/io/FileReader.hxx"
|
||||
#include "fs/io/BufferedReader.hxx"
|
||||
#include "io/FileReader.hxx"
|
||||
#include "io/BufferedReader.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <cassert>
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "config/Param.hxx"
|
||||
#include "config/Block.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/StandardDirectory.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
@@ -51,17 +52,30 @@ CreateConfiguredDatabase(const ConfigData &config,
|
||||
} else {
|
||||
/* if there is no override, use the cache directory */
|
||||
|
||||
const AllocatedPath cache_dir = GetUserCacheDir();
|
||||
const AllocatedPath cache_dir = GetAppCacheDir();
|
||||
if (cache_dir.IsNull())
|
||||
return nullptr;
|
||||
|
||||
const auto db_file = cache_dir / Path::FromFS(PATH_LITERAL("mpd.db"));
|
||||
const auto db_file = cache_dir / Path::FromFS(PATH_LITERAL("db"));
|
||||
auto db_file_utf8 = db_file.ToUTF8();
|
||||
if (db_file_utf8.empty())
|
||||
return nullptr;
|
||||
|
||||
ConfigBlock block;
|
||||
block.AddBlockParam("path", std::move(db_file_utf8), -1);
|
||||
|
||||
{
|
||||
const auto mounts_dir = cache_dir
|
||||
/ Path::FromFS(PATH_LITERAL("mounts"));
|
||||
CreateDirectoryNoThrow(mounts_dir);
|
||||
|
||||
if (auto mounts_dir_utf8 = mounts_dir.ToUTF8();
|
||||
!mounts_dir_utf8.empty())
|
||||
block.AddBlockParam("cache_directory",
|
||||
std::move(mounts_dir_utf8),
|
||||
-1);
|
||||
}
|
||||
|
||||
return DatabaseGlobalInit(main_event_loop, io_event_loop,
|
||||
listener, block);
|
||||
}
|
||||
|
@@ -20,8 +20,8 @@
|
||||
#include "DatabaseSave.hxx"
|
||||
#include "db/DatabaseLock.hxx"
|
||||
#include "DirectorySave.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "io/BufferedOutputStream.hxx"
|
||||
#include "io/LineReader.hxx"
|
||||
#include "tag/ParseName.hxx"
|
||||
#include "tag/Settings.hxx"
|
||||
#include "fs/Charset.hxx"
|
||||
@@ -64,7 +64,7 @@ db_save_internal(BufferedOutputStream &os, const Directory &music_root)
|
||||
}
|
||||
|
||||
void
|
||||
db_load_internal(TextFile &file, Directory &music_root)
|
||||
db_load_internal(LineReader &file, Directory &music_root)
|
||||
{
|
||||
char *line;
|
||||
unsigned format = 0;
|
||||
|
@@ -22,7 +22,7 @@
|
||||
|
||||
struct Directory;
|
||||
class BufferedOutputStream;
|
||||
class TextFile;
|
||||
class LineReader;
|
||||
|
||||
void
|
||||
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.
|
||||
*/
|
||||
void
|
||||
db_load_internal(TextFile &file, Directory &root);
|
||||
db_load_internal(LineReader &file, Directory &root);
|
||||
|
||||
#endif
|
||||
|
@@ -23,8 +23,8 @@
|
||||
#include "SongSave.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
#include "PlaylistDatabase.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "io/LineReader.hxx"
|
||||
#include "io/BufferedOutputStream.hxx"
|
||||
#include "time/ChronoUtil.hxx"
|
||||
#include "util/StringAPI.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
@@ -121,7 +121,7 @@ ParseLine(Directory &directory, const char *line)
|
||||
}
|
||||
|
||||
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)
|
||||
throw FormatRuntimeError("Duplicate subdirectory '%.*s'",
|
||||
@@ -152,7 +152,7 @@ directory_load_subdir(TextFile &file, Directory &parent, std::string_view name)
|
||||
}
|
||||
|
||||
void
|
||||
directory_load(TextFile &file, Directory &directory)
|
||||
directory_load(LineReader &file, Directory &directory)
|
||||
{
|
||||
const char *line;
|
||||
|
||||
|
@@ -21,7 +21,7 @@
|
||||
#define MPD_DIRECTORY_SAVE_HXX
|
||||
|
||||
struct Directory;
|
||||
class TextFile;
|
||||
class LineReader;
|
||||
class BufferedOutputStream;
|
||||
|
||||
void
|
||||
@@ -31,6 +31,6 @@ directory_save(BufferedOutputStream &os, const Directory &directory);
|
||||
* Throws #std::runtime_error on error.
|
||||
*/
|
||||
void
|
||||
directory_load(TextFile &file, Directory &directory);
|
||||
directory_load(LineReader &file, Directory &directory);
|
||||
|
||||
#endif
|
||||
|
@@ -34,8 +34,8 @@
|
||||
#include "db/DatabaseLock.hxx"
|
||||
#include "db/DatabaseError.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "fs/io/FileOutputStream.hxx"
|
||||
#include "io/BufferedOutputStream.hxx"
|
||||
#include "io/FileOutputStream.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "config/Block.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
@@ -46,7 +46,7 @@
|
||||
#include "Log.hxx"
|
||||
|
||||
#ifdef ENABLE_ZLIB
|
||||
#include "fs/io/GzipOutputStream.hxx"
|
||||
#include "lib/zlib/GzipOutputStream.hxx"
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
|
@@ -257,6 +257,12 @@ public:
|
||||
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
|
||||
* DecoderState::DECODE.
|
||||
|
@@ -20,7 +20,7 @@
|
||||
#ifndef MPD_DECODER_READER_HXX
|
||||
#define MPD_DECODER_READER_HXX
|
||||
|
||||
#include "fs/io/Reader.hxx"
|
||||
#include "io/Reader.hxx"
|
||||
|
||||
class DecoderClient;
|
||||
class InputStream;
|
||||
|
@@ -36,6 +36,7 @@
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "thread/Name.hxx"
|
||||
#include "tag/ApeReplayGain.hxx"
|
||||
#include "Log.hxx"
|
||||
@@ -261,12 +262,16 @@ LoadReplayGain(DecoderClient &client, InputStream &is)
|
||||
static void
|
||||
MaybeLoadReplayGain(DecoderBridge &bridge, InputStream &is)
|
||||
{
|
||||
{
|
||||
const std::scoped_lock<Mutex> protect(bridge.dc.mutex);
|
||||
if (bridge.dc.replay_gain_mode == ReplayGainMode::OFF)
|
||||
/* ReplayGain is disabled */
|
||||
return;
|
||||
}
|
||||
if (!bridge.dc.LockIsReplayGainEnabled())
|
||||
/* ReplayGain is disabled */
|
||||
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);
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#include "lib/ffmpeg/Format.hxx"
|
||||
#include "lib/ffmpeg/Codec.hxx"
|
||||
#include "lib/ffmpeg/SampleFormat.hxx"
|
||||
#include "lib/ffmpeg/LibFmt.hxx"
|
||||
#include "../DecoderAPI.hxx"
|
||||
#include "FfmpegMetaData.hxx"
|
||||
#include "FfmpegIo.hxx"
|
||||
@@ -384,7 +385,8 @@ static void
|
||||
FfmpegParseMetaData(const AVStream &stream,
|
||||
ReplayGainInfo &rg, MixRampInfo &mr)
|
||||
{
|
||||
FfmpegParseMetaData(*stream.metadata, rg, mr);
|
||||
if (stream.metadata != nullptr)
|
||||
FfmpegParseMetaData(*stream.metadata, rg, mr);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -393,7 +395,9 @@ FfmpegParseMetaData(const AVFormatContext &format_context, int audio_stream,
|
||||
{
|
||||
assert(audio_stream >= 0);
|
||||
|
||||
FfmpegParseMetaData(*format_context.metadata, rg, mr);
|
||||
if (format_context.metadata != nullptr)
|
||||
FfmpegParseMetaData(*format_context.metadata, rg, mr);
|
||||
|
||||
FfmpegParseMetaData(*format_context.streams[audio_stream],
|
||||
rg, mr);
|
||||
}
|
||||
@@ -468,7 +472,7 @@ static bool
|
||||
IsSeekable(const AVFormatContext &format_context) noexcept
|
||||
{
|
||||
#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
|
||||
(void)format_context;
|
||||
return false;
|
||||
@@ -520,9 +524,15 @@ FfmpegDecode(DecoderClient &client, InputStream *input,
|
||||
return;
|
||||
}
|
||||
|
||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 25, 100)
|
||||
const unsigned channels = codec_context->ch_layout.nb_channels;
|
||||
#else
|
||||
const unsigned channels = codec_context->channels;
|
||||
#endif
|
||||
|
||||
const auto audio_format = CheckAudioFormat(codec_context->sample_rate,
|
||||
sample_format,
|
||||
codec_context->channels);
|
||||
channels);
|
||||
|
||||
const SignedSongTime total_time =
|
||||
av_stream.duration != (int64_t)AV_NOPTS_VALUE
|
||||
@@ -530,9 +540,8 @@ FfmpegDecode(DecoderClient &client, InputStream *input,
|
||||
: FromFfmpegTimeChecked(format_context.duration, AV_TIME_BASE_Q);
|
||||
|
||||
client.Ready(audio_format,
|
||||
input
|
||||
? input->IsSeekable()
|
||||
: IsSeekable(format_context),
|
||||
(input ? input->IsSeekable() : false)
|
||||
|| IsSeekable(format_context),
|
||||
total_time);
|
||||
|
||||
FfmpegParseMetaData(client, format_context, audio_stream);
|
||||
@@ -633,10 +642,17 @@ FfmpegScanStream(AVFormatContext &format_context, TagHandler &handler)
|
||||
AV_TIME_BASE_Q));
|
||||
|
||||
const auto &codec_params = *stream.codecpar;
|
||||
|
||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 25, 100)
|
||||
const unsigned channels = codec_params.ch_layout.nb_channels;
|
||||
#else
|
||||
const unsigned channels = codec_params.channels;
|
||||
#endif
|
||||
|
||||
try {
|
||||
handler.OnAudioFormat(CheckAudioFormat(codec_params.sample_rate,
|
||||
ffmpeg_sample_format(AVSampleFormat(codec_params.format)),
|
||||
codec_params.channels));
|
||||
channels));
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
|
@@ -24,6 +24,10 @@
|
||||
#include "../DecoderAPI.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/mem.h>
|
||||
}
|
||||
|
||||
AvioStream::~AvioStream()
|
||||
{
|
||||
if (io != nullptr) {
|
||||
@@ -35,7 +39,11 @@ AvioStream::~AvioStream()
|
||||
inline int
|
||||
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
|
||||
|
@@ -91,7 +91,8 @@ ScanOpusTags(const void *data, size_t size,
|
||||
if (!r.Expect("OpusTags", 8))
|
||||
return false;
|
||||
|
||||
if (!handler.WantPair() && !handler.WantTag())
|
||||
if (!handler.WantPair() && !handler.WantTag() &&
|
||||
!handler.WantPicture())
|
||||
return true;
|
||||
|
||||
if (!r.SkipString())
|
||||
|
@@ -27,7 +27,7 @@
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "lib/icu/Converter.hxx"
|
||||
#ifdef HAVE_SIDPLAYFP
|
||||
#include "fs/io/FileReader.hxx"
|
||||
#include "io/FileReader.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#endif
|
||||
#include "util/StringFormat.hxx"
|
||||
|
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "ToOutputStream.hxx"
|
||||
#include "EncoderInterface.hxx"
|
||||
#include "fs/io/OutputStream.hxx"
|
||||
#include "io/OutputStream.hxx"
|
||||
|
||||
void
|
||||
EncoderToOutputStream(OutputStream &os, Encoder &encoder)
|
||||
|
@@ -3,6 +3,23 @@ encoder_features = configuration_data()
|
||||
encoder_features.set('ENABLE_ENCODER', 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)
|
||||
configure_file(output: 'Features.h', configuration: encoder_features)
|
||||
subdir_done()
|
||||
|
@@ -38,6 +38,7 @@ class FlacEncoder final : public Encoder {
|
||||
|
||||
FLAC__StreamEncoder *const fse;
|
||||
const unsigned compression;
|
||||
const bool oggflac;
|
||||
|
||||
PcmBuffer expand_buffer;
|
||||
|
||||
@@ -122,7 +123,7 @@ flac_encoder_init(const ConfigBlock &block)
|
||||
}
|
||||
|
||||
static void
|
||||
flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression,
|
||||
flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression, bool oggflac,
|
||||
const AudioFormat &audio_format)
|
||||
{
|
||||
unsigned bits_per_sample;
|
||||
@@ -157,7 +158,7 @@ flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression,
|
||||
throw FormatRuntimeError("error setting flac sample rate to %d",
|
||||
audio_format.sample_rate);
|
||||
|
||||
if (!FLAC__stream_encoder_set_ogg_serial_number(fse,
|
||||
if (oggflac && !FLAC__stream_encoder_set_ogg_serial_number(fse,
|
||||
GenerateSerial()))
|
||||
throw FormatRuntimeError("error setting ogg serial number");
|
||||
}
|
||||
@@ -166,11 +167,12 @@ FlacEncoder::FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse, u
|
||||
:Encoder(_oggchaining),
|
||||
audio_format(_audio_format), fse(_fse),
|
||||
compression(_compression),
|
||||
oggflac(_oggflac),
|
||||
output_buffer(8192)
|
||||
{
|
||||
/* this immediately outputs data through callback */
|
||||
|
||||
auto init_status = _oggflac ?
|
||||
auto init_status = oggflac ?
|
||||
FLAC__stream_encoder_init_ogg_stream(fse,
|
||||
nullptr, WriteCallback,
|
||||
nullptr, nullptr, nullptr,
|
||||
@@ -209,7 +211,7 @@ PreparedFlacEncoder::Open(AudioFormat &audio_format)
|
||||
throw std::runtime_error("FLAC__stream_encoder_new() failed");
|
||||
|
||||
try {
|
||||
flac_encoder_setup(fse, compression, audio_format);
|
||||
flac_encoder_setup(fse, compression, oggflac, audio_format);
|
||||
} catch (...) {
|
||||
FLAC__stream_encoder_delete(fse);
|
||||
throw;
|
||||
@@ -222,7 +224,7 @@ void
|
||||
FlacEncoder::SendTag(const Tag &tag)
|
||||
{
|
||||
/* re-initialize encoder since flac_encoder_finish resets everything */
|
||||
flac_encoder_setup(fse, compression, audio_format);
|
||||
flac_encoder_setup(fse, compression, oggflac, audio_format);
|
||||
|
||||
FLAC__StreamMetadata *metadata = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
||||
FLAC__StreamMetadata_VorbisComment_Entry entry;
|
||||
|
@@ -35,7 +35,7 @@ if libshine_dep.found()
|
||||
endif
|
||||
|
||||
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'
|
||||
endif
|
||||
|
||||
|
@@ -40,10 +40,15 @@ FfmpegFilter::FfmpegFilter(const AudioFormat &in_audio_format,
|
||||
buffer_sink(_buffer_sink),
|
||||
in_format(Ffmpeg::ToFfmpegSampleFormat(in_audio_format.format)),
|
||||
in_sample_rate(in_audio_format.sample_rate),
|
||||
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 25, 100)
|
||||
in_channels(in_audio_format.channels),
|
||||
#endif
|
||||
in_audio_frame_size(in_audio_format.GetFrameSize()),
|
||||
out_audio_frame_size(_out_audio_format.GetFrameSize())
|
||||
{
|
||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 25, 100)
|
||||
av_channel_layout_default(&in_ch_layout, in_audio_format.channels);
|
||||
#endif
|
||||
}
|
||||
|
||||
ConstBuffer<void>
|
||||
@@ -54,7 +59,11 @@ FfmpegFilter::FilterPCM(ConstBuffer<void> src)
|
||||
frame.Unref();
|
||||
frame->format = in_format;
|
||||
frame->sample_rate = in_sample_rate;
|
||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 25, 100)
|
||||
frame->ch_layout = in_ch_layout;
|
||||
#else
|
||||
frame->channels = in_channels;
|
||||
#endif
|
||||
frame->nb_samples = src.size / in_audio_frame_size;
|
||||
|
||||
frame.GetBuffer();
|
||||
|
@@ -35,7 +35,13 @@ class FfmpegFilter final : public Filter {
|
||||
|
||||
FfmpegBuffer interleave_buffer;
|
||||
|
||||
const int in_format, in_sample_rate, in_channels;
|
||||
const int in_format, in_sample_rate;
|
||||
|
||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 25, 100)
|
||||
AVChannelLayout in_ch_layout;
|
||||
#else
|
||||
const int in_channels;
|
||||
#endif
|
||||
|
||||
const size_t in_audio_frame_size;
|
||||
const size_t out_audio_frame_size;
|
||||
|
@@ -23,7 +23,7 @@
|
||||
#include "filter/Prepared.hxx"
|
||||
#include "pcm/Buffer.hxx"
|
||||
#include "pcm/AudioFormat.hxx"
|
||||
#include "AudioCompress/compress.h"
|
||||
#include "pcm/AudioCompress/compress.h"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
|
||||
#include <string.h>
|
||||
|
@@ -23,6 +23,8 @@
|
||||
#include "ReplayGainInfo.hxx"
|
||||
#include "ReplayGainConfig.hxx"
|
||||
#include "mixer/MixerControl.hxx"
|
||||
#include "mixer/MixerInternal.hxx"
|
||||
#include "mixer/Listener.hxx"
|
||||
#include "pcm/AudioFormat.hxx"
|
||||
#include "pcm/Volume.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
@@ -171,9 +173,11 @@ ReplayGainFilter::Update()
|
||||
try {
|
||||
mixer_set_volume(mixer, _volume);
|
||||
|
||||
/* TODO: emit this idle event only for the
|
||||
current partition */
|
||||
idle_add(IDLE_MIXER);
|
||||
/* invoke the mixer's listener manually, just
|
||||
in case the mixer implementation didn't do
|
||||
that already (this depends on the
|
||||
implementation) */
|
||||
mixer->listener.OnMixerVolumeChanged(*mixer, _volume);
|
||||
} catch (...) {
|
||||
LogError(std::current_exception(),
|
||||
"Failed to update hardware mixer");
|
||||
|
@@ -12,7 +12,6 @@ endif
|
||||
|
||||
filter_plugins = static_library(
|
||||
'filter_plugins',
|
||||
'../../AudioCompress/compress.c',
|
||||
'NullFilterPlugin.cxx',
|
||||
'TwoFilters.cxx',
|
||||
'AutoConvertFilterPlugin.cxx',
|
||||
|
@@ -67,6 +67,16 @@ StatFile(Path file, struct stat &buf, bool follow_symlinks = true)
|
||||
|
||||
#endif
|
||||
|
||||
static inline bool
|
||||
CreateDirectoryNoThrow(Path path) noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return CreateDirectory(path.c_str(), nullptr);
|
||||
#else
|
||||
return mkdir(path.c_str(), 0777);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate a file that exists already. Throws std::system_error on
|
||||
* error.
|
||||
|
@@ -41,7 +41,7 @@
|
||||
#ifdef USE_XDG
|
||||
#include "util/StringStrip.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "io/TextFile.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include <string.h>
|
||||
#include <utility>
|
||||
#endif
|
||||
@@ -53,6 +53,12 @@
|
||||
#include "Main.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef USE_XDG
|
||||
#include "Version.h" // for PACKAGE_NAME
|
||||
#define APP_FILENAME PATH_LITERAL(PACKAGE_NAME)
|
||||
static constexpr Path app_filename = Path::FromFS(APP_FILENAME);
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(ANDROID)
|
||||
class PasswdEntry
|
||||
{
|
||||
@@ -254,7 +260,8 @@ GetUserMusicDir() noexcept
|
||||
#elif defined(USE_XDG)
|
||||
return GetUserDir("XDG_MUSIC_DIR");
|
||||
#elif defined(ANDROID)
|
||||
return Environment::getExternalStoragePublicDirectory("Music");
|
||||
return Environment::getExternalStoragePublicDirectory(Java::GetEnv(),
|
||||
"Music");
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
@@ -283,6 +290,24 @@ GetUserCacheDir() noexcept
|
||||
#endif
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
GetAppCacheDir() noexcept
|
||||
{
|
||||
#ifdef USE_XDG
|
||||
if (const auto user_dir = GetUserCacheDir(); !user_dir.IsNull()) {
|
||||
auto dir = user_dir / app_filename;
|
||||
CreateDirectoryNoThrow(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
#elif defined(ANDROID)
|
||||
return context->GetCacheDir(Java::GetEnv());
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
AllocatedPath
|
||||
GetUserRuntimeDir() noexcept
|
||||
{
|
||||
@@ -296,7 +321,7 @@ GetUserRuntimeDir() noexcept
|
||||
AllocatedPath
|
||||
GetAppRuntimeDir() noexcept
|
||||
{
|
||||
#ifdef __linux__
|
||||
#if defined(__linux__) && !defined(ANDROID)
|
||||
/* systemd specific; see systemd.exec(5) */
|
||||
if (const char *runtime_directory = getenv("RUNTIME_DIRECTORY"))
|
||||
if (auto dir = StringView{runtime_directory}.Split(':').first;
|
||||
@@ -306,8 +331,8 @@ GetAppRuntimeDir() noexcept
|
||||
|
||||
#ifdef USE_XDG
|
||||
if (const auto user_dir = GetUserRuntimeDir(); !user_dir.IsNull()) {
|
||||
auto dir = user_dir / Path::FromFS("mpd");
|
||||
mkdir(dir.c_str(), 0700);
|
||||
auto dir = user_dir / app_filename;
|
||||
CreateDirectoryNoThrow(dir);
|
||||
return dir;
|
||||
}
|
||||
#endif
|
||||
|
@@ -43,6 +43,13 @@ GetUserMusicDir() noexcept;
|
||||
AllocatedPath
|
||||
GetUserCacheDir() noexcept;
|
||||
|
||||
/**
|
||||
* Obtains cache directory for this application.
|
||||
*/
|
||||
[[gnu::const]]
|
||||
AllocatedPath
|
||||
GetAppCacheDir() noexcept;
|
||||
|
||||
/**
|
||||
* Obtains the runtime directory for the current user.
|
||||
*/
|
||||
|
@@ -18,9 +18,9 @@
|
||||
*/
|
||||
|
||||
#include "TextFile.hxx"
|
||||
#include "FileReader.hxx"
|
||||
#include "AutoGunzipReader.hxx"
|
||||
#include "BufferedReader.hxx"
|
||||
#include "io/FileReader.hxx"
|
||||
#include "io/BufferedReader.hxx"
|
||||
#include "lib/zlib/AutoGunzipReader.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
|
||||
#include <cassert>
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#ifndef MPD_TEXT_FILE_HXX
|
||||
#define MPD_TEXT_FILE_HXX
|
||||
|
||||
#include "io/LineReader.hxx"
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -29,7 +30,7 @@ class FileReader;
|
||||
class AutoGunzipReader;
|
||||
class BufferedReader;
|
||||
|
||||
class TextFile {
|
||||
class TextFile final : public LineReader {
|
||||
const std::unique_ptr<FileReader> file_reader;
|
||||
|
||||
#ifdef ENABLE_ZLIB
|
||||
@@ -45,14 +46,8 @@ public:
|
||||
|
||||
~TextFile() noexcept;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
char *ReadLine();
|
||||
/* virtual methods from class LineReader */
|
||||
char *ReadLine() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -14,12 +14,7 @@ fs_sources = [
|
||||
'CheckFile.cxx',
|
||||
'LookupFile.cxx',
|
||||
'DirectoryReader.cxx',
|
||||
'io/PeekReader.cxx',
|
||||
'io/FileReader.cxx',
|
||||
'io/BufferedReader.cxx',
|
||||
'io/TextFile.cxx',
|
||||
'io/FileOutputStream.cxx',
|
||||
'io/BufferedOutputStream.cxx',
|
||||
]
|
||||
|
||||
if is_windows
|
||||
@@ -28,14 +23,6 @@ else
|
||||
shlwapi_dep = dependency('', required: false)
|
||||
endif
|
||||
|
||||
if zlib_dep.found()
|
||||
fs_sources += [
|
||||
'io/GunzipReader.cxx',
|
||||
'io/AutoGunzipReader.cxx',
|
||||
'io/GzipOutputStream.cxx',
|
||||
]
|
||||
endif
|
||||
|
||||
fs = static_library(
|
||||
'fs',
|
||||
fs_sources,
|
||||
|
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "IcyInputStream.hxx"
|
||||
#include "IcyMetaDataParser.hxx"
|
||||
#include "tag/IcyMetaDataParser.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "util/UriExtract.hxx"
|
||||
#include "util/UriQueryParser.hxx"
|
||||
|
@@ -20,7 +20,7 @@
|
||||
#ifndef MPD_INPUT_READER_HXX
|
||||
#define MPD_INPUT_READER_HXX
|
||||
|
||||
#include "fs/io/Reader.hxx"
|
||||
#include "io/Reader.hxx"
|
||||
|
||||
class InputStream;
|
||||
|
||||
|
@@ -30,10 +30,12 @@
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/ByteOrder.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "config/Block.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
@@ -43,31 +45,35 @@
|
||||
|
||||
#include <cdio/cd_types.h>
|
||||
|
||||
static constexpr Domain cdio_domain("cdio");
|
||||
|
||||
static bool default_reverse_endian;
|
||||
static unsigned speed = 0;
|
||||
|
||||
/* Default to full paranoia, but allow skipping sectors. */
|
||||
static int mode_flags = PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP;
|
||||
|
||||
class CdioParanoiaInputStream final : public InputStream {
|
||||
cdrom_drive_t *const drv;
|
||||
CdIo_t *const cdio;
|
||||
CdromParanoia para;
|
||||
|
||||
const lsn_t lsn_from, lsn_to;
|
||||
int lsn_relofs;
|
||||
const lsn_t lsn_from;
|
||||
|
||||
char buffer[CDIO_CD_FRAMESIZE_RAW];
|
||||
int buffer_lsn;
|
||||
lsn_t buffer_lsn;
|
||||
|
||||
public:
|
||||
CdioParanoiaInputStream(const char *_uri, Mutex &_mutex,
|
||||
cdrom_drive_t *_drv, CdIo_t *_cdio,
|
||||
bool reverse_endian,
|
||||
lsn_t _lsn_from, lsn_t _lsn_to)
|
||||
lsn_t _lsn_from, lsn_t lsn_to)
|
||||
:InputStream(_uri, _mutex),
|
||||
drv(_drv), cdio(_cdio), para(drv),
|
||||
lsn_from(_lsn_from), lsn_to(_lsn_to),
|
||||
lsn_relofs(0),
|
||||
lsn_from(_lsn_from),
|
||||
buffer_lsn(-1)
|
||||
{
|
||||
/* Set reading mode for full paranoia, but allow
|
||||
skipping sectors. */
|
||||
para.SetMode(PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP);
|
||||
para.SetMode(mode_flags);
|
||||
|
||||
/* seek to beginning of the track */
|
||||
para.Seek(lsn_from);
|
||||
@@ -98,11 +104,6 @@ class CdioParanoiaInputStream final : public InputStream {
|
||||
void Seek(std::unique_lock<Mutex> &lock, offset_type offset) override;
|
||||
};
|
||||
|
||||
static constexpr Domain cdio_domain("cdio");
|
||||
|
||||
static bool default_reverse_endian;
|
||||
static unsigned speed = 0;
|
||||
|
||||
static void
|
||||
input_cdio_init(EventLoop &, const ConfigBlock &block)
|
||||
{
|
||||
@@ -117,6 +118,26 @@ input_cdio_init(EventLoop &, const ConfigBlock &block)
|
||||
value);
|
||||
}
|
||||
speed = block.GetBlockValue("speed",0U);
|
||||
|
||||
if (const auto *param = block.GetBlockParam("mode")) {
|
||||
param->With([](const char *s){
|
||||
if (StringIsEqual(s, "disable"))
|
||||
mode_flags = PARANOIA_MODE_DISABLE;
|
||||
else if (StringIsEqual(s, "overlap"))
|
||||
mode_flags = PARANOIA_MODE_OVERLAP;
|
||||
else if (StringIsEqual(s, "full"))
|
||||
mode_flags = PARANOIA_MODE_FULL;
|
||||
else
|
||||
throw std::invalid_argument{"Invalid paranoia mode"};
|
||||
});
|
||||
}
|
||||
|
||||
if (const auto *param = block.GetBlockParam("skip")) {
|
||||
if (param->GetBoolValue())
|
||||
mode_flags &= ~PARANOIA_MODE_NEVERSKIP;
|
||||
else
|
||||
mode_flags |= PARANOIA_MODE_NEVERSKIP;
|
||||
}
|
||||
}
|
||||
|
||||
struct CdioUri {
|
||||
@@ -173,9 +194,12 @@ cdio_detect_device()
|
||||
if (devices == nullptr)
|
||||
return nullptr;
|
||||
|
||||
AllocatedPath path = AllocatedPath::FromFS(devices[0]);
|
||||
cdio_free_device_list(devices);
|
||||
return path;
|
||||
AtScopeExit(devices) { cdio_free_device_list(devices); };
|
||||
|
||||
if (devices[0] == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return AllocatedPath::FromFS(devices[0]);
|
||||
}
|
||||
|
||||
static InputStreamPtr
|
||||
@@ -271,81 +295,70 @@ CdioParanoiaInputStream::Seek(std::unique_lock<Mutex> &,
|
||||
return;
|
||||
|
||||
/* calculate current LSN */
|
||||
lsn_relofs = new_offset / CDIO_CD_FRAMESIZE_RAW;
|
||||
offset = new_offset;
|
||||
const lsn_t lsn_relofs = new_offset / CDIO_CD_FRAMESIZE_RAW;
|
||||
|
||||
{
|
||||
if (lsn_relofs != buffer_lsn) {
|
||||
const ScopeUnlock unlock(mutex);
|
||||
para.Seek(lsn_from + lsn_relofs);
|
||||
}
|
||||
|
||||
offset = new_offset;
|
||||
}
|
||||
|
||||
size_t
|
||||
CdioParanoiaInputStream::Read(std::unique_lock<Mutex> &,
|
||||
void *ptr, size_t length)
|
||||
{
|
||||
size_t nbytes = 0;
|
||||
char *wptr = (char *) ptr;
|
||||
/* end of track ? */
|
||||
if (IsEOF())
|
||||
return 0;
|
||||
|
||||
while (length > 0) {
|
||||
/* end of track ? */
|
||||
if (lsn_from + lsn_relofs > lsn_to)
|
||||
break;
|
||||
//current sector was changed ?
|
||||
const int16_t *rbuf;
|
||||
|
||||
//current sector was changed ?
|
||||
const int16_t *rbuf;
|
||||
if (lsn_relofs != buffer_lsn) {
|
||||
const ScopeUnlock unlock(mutex);
|
||||
const lsn_t lsn_relofs = offset / CDIO_CD_FRAMESIZE_RAW;
|
||||
const std::size_t diff = offset % CDIO_CD_FRAMESIZE_RAW;
|
||||
|
||||
try {
|
||||
rbuf = para.Read().data;
|
||||
} catch (...) {
|
||||
char *s_err = cdio_cddap_errors(drv);
|
||||
if (s_err) {
|
||||
FmtError(cdio_domain,
|
||||
"paranoia_read: {}", s_err);
|
||||
cdio_cddap_free_messages(s_err);
|
||||
}
|
||||
if (lsn_relofs != buffer_lsn) {
|
||||
const ScopeUnlock unlock(mutex);
|
||||
|
||||
throw;
|
||||
try {
|
||||
rbuf = para.Read().data;
|
||||
} catch (...) {
|
||||
char *s_err = cdio_cddap_errors(drv);
|
||||
if (s_err) {
|
||||
FmtError(cdio_domain,
|
||||
"paranoia_read: {}", s_err);
|
||||
cdio_cddap_free_messages(s_err);
|
||||
}
|
||||
|
||||
//store current buffer
|
||||
memcpy(buffer, rbuf, CDIO_CD_FRAMESIZE_RAW);
|
||||
buffer_lsn = lsn_relofs;
|
||||
} else {
|
||||
//use cached sector
|
||||
rbuf = (const int16_t *)buffer;
|
||||
throw;
|
||||
}
|
||||
|
||||
//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 len = (length < maxwrite? length : maxwrite);
|
||||
|
||||
//skip diff bytes from this lsn
|
||||
memcpy(wptr, ((const char *)rbuf) + diff, len);
|
||||
//update pointer
|
||||
wptr += len;
|
||||
nbytes += len;
|
||||
|
||||
//update offset
|
||||
offset += len;
|
||||
lsn_relofs = offset / CDIO_CD_FRAMESIZE_RAW;
|
||||
//update length
|
||||
length -= len;
|
||||
//store current buffer
|
||||
memcpy(buffer, rbuf, CDIO_CD_FRAMESIZE_RAW);
|
||||
buffer_lsn = lsn_relofs;
|
||||
} else {
|
||||
//use cached sector
|
||||
rbuf = (const int16_t *)buffer;
|
||||
}
|
||||
|
||||
const size_t maxwrite = CDIO_CD_FRAMESIZE_RAW - diff; //# of bytes pending in current buffer
|
||||
const std::size_t nbytes = std::min(length, maxwrite);
|
||||
|
||||
//skip diff bytes from this lsn
|
||||
memcpy(ptr, ((const char *)rbuf) + diff, nbytes);
|
||||
|
||||
//update offset
|
||||
offset += nbytes;
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
bool
|
||||
CdioParanoiaInputStream::IsEOF() const noexcept
|
||||
{
|
||||
return lsn_from + lsn_relofs > lsn_to;
|
||||
return offset >= size;
|
||||
}
|
||||
|
||||
static constexpr const char *cdio_paranoia_prefixes[] = {
|
||||
|
@@ -27,7 +27,7 @@
|
||||
#include "../MaybeBufferedInputStream.hxx"
|
||||
#include "../AsyncInputStream.hxx"
|
||||
#include "../IcyInputStream.hxx"
|
||||
#include "IcyMetaDataParser.hxx"
|
||||
#include "tag/IcyMetaDataParser.hxx"
|
||||
#include "../InputPlugin.hxx"
|
||||
#include "config/Block.hxx"
|
||||
#include "tag/Builder.hxx"
|
||||
@@ -82,7 +82,7 @@ class CurlInputStream final : public AsyncInputStream, CurlResponseHandler {
|
||||
public:
|
||||
template<typename I>
|
||||
CurlInputStream(EventLoop &event_loop, const char *_url,
|
||||
const std::multimap<std::string, std::string> &headers,
|
||||
const Curl::Headers &headers,
|
||||
I &&_icy,
|
||||
Mutex &_mutex);
|
||||
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
CurlInputStream &operator=(const CurlInputStream &) = delete;
|
||||
|
||||
static InputStreamPtr Open(const char *url,
|
||||
const std::multimap<std::string, std::string> &headers,
|
||||
const Curl::Headers &headers,
|
||||
Mutex &mutex);
|
||||
|
||||
private:
|
||||
@@ -131,8 +131,7 @@ private:
|
||||
void SeekInternal(offset_type new_offset);
|
||||
|
||||
/* virtual methods from CurlResponseHandler */
|
||||
void OnHeaders(unsigned status,
|
||||
std::multimap<std::string, std::string> &&headers) override;
|
||||
void OnHeaders(unsigned status, Curl::Headers &&headers) override;
|
||||
void OnData(ConstBuffer<void> data) override;
|
||||
void OnEnd() override;
|
||||
void OnError(std::exception_ptr e) noexcept override;
|
||||
@@ -227,7 +226,7 @@ WithConvertedTagValue(const char *uri, const char *value, F &&f) noexcept
|
||||
|
||||
void
|
||||
CurlInputStream::OnHeaders(unsigned status,
|
||||
std::multimap<std::string, std::string> &&headers)
|
||||
Curl::Headers &&headers)
|
||||
{
|
||||
assert(GetEventLoop().IsInside());
|
||||
assert(!postponed_exception);
|
||||
@@ -391,7 +390,7 @@ input_curl_finish() noexcept
|
||||
template<typename I>
|
||||
inline
|
||||
CurlInputStream::CurlInputStream(EventLoop &event_loop, const char *_url,
|
||||
const std::multimap<std::string, std::string> &headers,
|
||||
const Curl::Headers &headers,
|
||||
I &&_icy,
|
||||
Mutex &_mutex)
|
||||
:AsyncInputStream(event_loop, _url, _mutex,
|
||||
@@ -440,6 +439,14 @@ CurlInputStream::InitEasy()
|
||||
request->SetVerifyPeer(verify_peer);
|
||||
request->SetVerifyHost(verify_host);
|
||||
request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get());
|
||||
|
||||
try {
|
||||
request->SetProxyVerifyPeer(verify_peer);
|
||||
request->SetProxyVerifyHost(verify_host);
|
||||
} catch (...) {
|
||||
/* these methods fail if libCURL was compiled with
|
||||
CURL_DISABLE_PROXY; ignore silently */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -491,7 +498,7 @@ CurlInputStream::DoSeek(offset_type new_offset)
|
||||
|
||||
inline InputStreamPtr
|
||||
CurlInputStream::Open(const char *url,
|
||||
const std::multimap<std::string, std::string> &headers,
|
||||
const Curl::Headers &headers,
|
||||
Mutex &mutex)
|
||||
{
|
||||
auto icy = std::make_shared<IcyMetaDataParser>();
|
||||
@@ -510,8 +517,7 @@ CurlInputStream::Open(const char *url,
|
||||
}
|
||||
|
||||
InputStreamPtr
|
||||
OpenCurlInputStream(const char *uri,
|
||||
const std::multimap<std::string, std::string> &headers,
|
||||
OpenCurlInputStream(const char *uri, const Curl::Headers &headers,
|
||||
Mutex &mutex)
|
||||
{
|
||||
return CurlInputStream::Open(uri, headers, mutex);
|
||||
|
@@ -20,12 +20,10 @@
|
||||
#ifndef MPD_INPUT_CURL_HXX
|
||||
#define MPD_INPUT_CURL_HXX
|
||||
|
||||
#include "lib/curl/Headers.hxx"
|
||||
#include "input/Ptr.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
extern const struct InputPlugin input_plugin_curl;
|
||||
|
||||
/**
|
||||
@@ -36,8 +34,7 @@ extern const struct InputPlugin input_plugin_curl;
|
||||
* Throws on error.
|
||||
*/
|
||||
InputStreamPtr
|
||||
OpenCurlInputStream(const char *uri,
|
||||
const std::multimap<std::string, std::string> &headers,
|
||||
OpenCurlInputStream(const char *uri, const Curl::Headers &headers,
|
||||
Mutex &mutex);
|
||||
|
||||
#endif
|
||||
|
@@ -21,7 +21,7 @@
|
||||
#include "../InputStream.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "fs/io/FileReader.hxx"
|
||||
#include "io/FileReader.hxx"
|
||||
#include "io/FileDescriptor.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
|
@@ -164,7 +164,7 @@ QobuzClient::InvokeHandlers() noexcept
|
||||
|
||||
std::string
|
||||
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());
|
||||
|
||||
@@ -183,7 +183,7 @@ QobuzClient::MakeUrl(const char *object, const char *method,
|
||||
|
||||
std::string
|
||||
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());
|
||||
|
||||
|
@@ -23,12 +23,12 @@
|
||||
#include "QobuzSession.hxx"
|
||||
#include "QobuzLoginRequest.hxx"
|
||||
#include "lib/curl/Init.hxx"
|
||||
#include "lib/curl/Headers.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "event/DeferEvent.hxx"
|
||||
#include "util/IntrusiveList.hxx"
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
class QobuzSessionHandler
|
||||
@@ -94,10 +94,10 @@ public:
|
||||
QobuzSession GetSession() const;
|
||||
|
||||
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,
|
||||
const std::multimap<std::string, std::string> &query) const noexcept;
|
||||
const Curl::Headers &query) const noexcept;
|
||||
|
||||
private:
|
||||
void StartLogin();
|
||||
|
@@ -38,7 +38,7 @@ static constexpr yajl_callbacks qobuz_error_parser_callbacks = {
|
||||
};
|
||||
|
||||
QobuzErrorParser::QobuzErrorParser(unsigned _status,
|
||||
const std::multimap<std::string, std::string> &headers)
|
||||
const Curl::Headers &headers)
|
||||
:YajlResponseParser(&qobuz_error_parser_callbacks, nullptr, this),
|
||||
status(_status)
|
||||
{
|
||||
|
@@ -20,11 +20,9 @@
|
||||
#ifndef QOBUZ_ERROR_PARSER_HXX
|
||||
#define QOBUZ_ERROR_PARSER_HXX
|
||||
|
||||
#include "lib/curl/Headers.hxx"
|
||||
#include "lib/yajl/ResponseParser.hxx"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
template<typename T> struct ConstBuffer;
|
||||
struct StringView;
|
||||
|
||||
@@ -46,8 +44,7 @@ public:
|
||||
* May throw if there is a formal error in the response
|
||||
* headers.
|
||||
*/
|
||||
QobuzErrorParser(unsigned status,
|
||||
const std::multimap<std::string, std::string> &headers);
|
||||
QobuzErrorParser(unsigned status, const Curl::Headers &headers);
|
||||
|
||||
protected:
|
||||
/* virtual methods from CurlResponseParser */
|
||||
|
@@ -77,7 +77,7 @@ QobuzLoginRequest::ResponseParser::GetSession()
|
||||
return std::move(session);
|
||||
}
|
||||
|
||||
static std::multimap<std::string, std::string>
|
||||
static Curl::Headers
|
||||
MakeLoginForm(const char *app_id,
|
||||
const char *username, const char *email,
|
||||
const char *password,
|
||||
@@ -85,7 +85,7 @@ MakeLoginForm(const char *app_id,
|
||||
{
|
||||
assert(username != nullptr || email != nullptr);
|
||||
|
||||
std::multimap<std::string, std::string> form{
|
||||
Curl::Headers form{
|
||||
{"app_id", app_id},
|
||||
{"password", password},
|
||||
{"device_manufacturer_id", device_manufacturer_id},
|
||||
@@ -134,8 +134,7 @@ QobuzLoginRequest::~QobuzLoginRequest() noexcept
|
||||
}
|
||||
|
||||
std::unique_ptr<CurlResponseParser>
|
||||
QobuzLoginRequest::MakeParser(unsigned status,
|
||||
std::multimap<std::string, std::string> &&headers)
|
||||
QobuzLoginRequest::MakeParser(unsigned status, Curl::Headers &&headers)
|
||||
{
|
||||
if (status != 200)
|
||||
return std::make_unique<QobuzErrorParser>(status, headers);
|
||||
|
@@ -56,7 +56,7 @@ public:
|
||||
private:
|
||||
/* virtual methods from DelegateCurlResponseHandler */
|
||||
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;
|
||||
|
||||
/* virtual methods from CurlResponseHandler */
|
||||
|
@@ -99,8 +99,7 @@ QobuzTagScanner::~QobuzTagScanner() noexcept
|
||||
}
|
||||
|
||||
std::unique_ptr<CurlResponseParser>
|
||||
QobuzTagScanner::MakeParser(unsigned status,
|
||||
std::multimap<std::string, std::string> &&headers)
|
||||
QobuzTagScanner::MakeParser(unsigned status, Curl::Headers &&headers)
|
||||
{
|
||||
if (status != 200)
|
||||
return std::make_unique<QobuzErrorParser>(status, headers);
|
||||
|
@@ -49,7 +49,7 @@ public:
|
||||
private:
|
||||
/* virtual methods from DelegateCurlResponseHandler */
|
||||
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;
|
||||
|
||||
/* virtual methods from CurlResponseHandler */
|
||||
|
@@ -93,7 +93,7 @@ QobuzTrackRequest::~QobuzTrackRequest() noexcept
|
||||
|
||||
std::unique_ptr<CurlResponseParser>
|
||||
QobuzTrackRequest::MakeParser(unsigned status,
|
||||
std::multimap<std::string, std::string> &&headers)
|
||||
Curl::Headers &&headers)
|
||||
{
|
||||
if (status != 200)
|
||||
return std::make_unique<QobuzErrorParser>(status, headers);
|
||||
|
@@ -56,7 +56,7 @@ public:
|
||||
private:
|
||||
/* virtual methods from DelegateCurlResponseHandler */
|
||||
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;
|
||||
|
||||
/* virtual methods from CurlResponseHandler */
|
||||
|
@@ -20,7 +20,7 @@ if curl_dep.found()
|
||||
input_plugins_sources += [
|
||||
'CurlInputPlugin.cxx',
|
||||
'../IcyInputStream.cxx',
|
||||
'../../IcyMetaDataParser.cxx',
|
||||
'../../tag/IcyMetaDataParser.cxx',
|
||||
]
|
||||
endif
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user