summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichał Cichoń <michcic@gmail.com>2017-05-18 18:25:59 +0200
committerMichał Cichoń <michcic@gmail.com>2017-05-18 18:25:59 +0200
commitcd5c65097ecf03480f477e47e0bb706dbf705a21 (patch)
tree21f4419d9dceae3226286585b8b5ca87ea1934ce
parent6dee813dad2dd0b741025be046dfe3c7019bddb9 (diff)
parent593b2d6f18eb21309b82f7cad4a56f7bb1218180 (diff)
downloadpianobar-windows-cd5c65097ecf03480f477e47e0bb706dbf705a21.tar.gz
pianobar-windows-cd5c65097ecf03480f477e47e0bb706dbf705a21.tar.bz2
pianobar-windows-cd5c65097ecf03480f477e47e0bb706dbf705a21.zip
Merge branch 'release/2017.05.18'2017.05.18
-rw-r--r--Makefile51
-rw-r--r--README.md25
-rw-r--r--appveyor.yml79
-rw-r--r--contrib/config-example2
-rwxr-xr-xcontrib/eventcmd-examples/eventcmd.sh2
-rw-r--r--contrib/pianobar.125
-rw-r--r--pianobar.cfg.example31
-rw-r--r--release/README.md34
-rw-r--r--release/pianobar.cfg.example31
-rw-r--r--screenshots/pianobar.pngbin0 -> 46647 bytes
-rw-r--r--src/config.h6
-rw-r--r--src/console.h4
-rw-r--r--src/http/http.c49
-rw-r--r--src/http/http.h5
-rw-r--r--src/libpiano/crypt.h4
-rw-r--r--src/libpiano/piano.h4
-rw-r--r--src/libpiano/piano_private.h4
-rw-r--r--src/libpiano/response.c179
-rw-r--r--src/main.c531
-rw-r--r--src/main.h9
-rw-r--r--src/player/backends/media_foundation.h7
-rw-r--r--src/player/backends/utility/com_ptr.h4
-rw-r--r--src/player/backends/utility/optional.h5
-rw-r--r--src/player/player2.h4
-rw-r--r--src/player/player2_private.h3
-rw-r--r--src/settings.c73
-rw-r--r--src/settings.h8
-rw-r--r--src/ui.c3
-rw-r--r--src/ui.h4
-rw-r--r--src/ui_act.c20
-rw-r--r--src/ui_act.h4
-rw-r--r--src/ui_dispatch.h5
-rw-r--r--src/ui_readline.h5
-rw-r--r--src/ui_types.h4
34 files changed, 790 insertions, 434 deletions
diff --git a/Makefile b/Makefile
index 78f1d43..f1dea03 100644
--- a/Makefile
+++ b/Makefile
@@ -84,26 +84,34 @@ ALL_LDFLAGS:=${LDFLAGS} -lao -lpthread -lm \
${LIBAV_LDFLAGS} ${LIBGNUTLS_LDFLAGS} \
${LIBGCRYPT_LDFLAGS} ${LIBJSONC_LDFLAGS} ${LIBCURL_LDFLAGS}
+# Be verbose if V=1 (gnu autotools’ --disable-silent-rules)
+SILENTCMD:=@
+SILENTECHO:=@echo
+ifeq (${V},1)
+ SILENTCMD:=
+ SILENTECHO:=@true
+endif
+
# build pianobar
ifeq (${DYNLINK},1)
pianobar: ${PIANOBAR_OBJ} ${PIANOBAR_HDR} libpiano.so.0
- @echo " LINK $@"
- @${CC} -o $@ ${PIANOBAR_OBJ} -L. -lpiano ${ALL_LDFLAGS}
+ ${SILENTECHO} " LINK $@"
+ ${SILENTCMD}${CC} -o $@ ${PIANOBAR_OBJ} -L. -lpiano ${ALL_LDFLAGS}
else
pianobar: ${PIANOBAR_OBJ} ${PIANOBAR_HDR} ${LIBPIANO_OBJ}
- @echo " LINK $@"
- @${CC} -o $@ ${PIANOBAR_OBJ} ${LIBPIANO_OBJ} ${ALL_LDFLAGS}
+ ${SILENTECHO} " LINK $@"
+ ${SILENTCMD}${CC} -o $@ ${PIANOBAR_OBJ} ${LIBPIANO_OBJ} ${ALL_LDFLAGS}
endif
# build shared and static libpiano
libpiano.so.0: ${LIBPIANO_RELOBJ} ${LIBPIANO_HDR} ${LIBPIANO_OBJ}
- @echo " LINK $@"
- @${CC} -shared -Wl,-soname,libpiano.so.0 -o libpiano.so.0.0.0 \
+ ${SILENTECHO} " LINK $@"
+ ${SILENTCMD}${CC} -shared -Wl,-soname,libpiano.so.0 -o libpiano.so.0.0.0 \
${LIBPIANO_RELOBJ} ${ALL_LDFLAGS}
- @ln -fs libpiano.so.0.0.0 libpiano.so.0
- @ln -fs libpiano.so.0 libpiano.so
- @echo " AR libpiano.a"
- @${AR} rcs libpiano.a ${LIBPIANO_OBJ}
+ ${SILENTCMD}ln -fs libpiano.so.0.0.0 libpiano.so.0
+ ${SILENTCMD}ln -fs libpiano.so.0 libpiano.so
+ ${SILENTECHO} " AR libpiano.a"
+ ${SILENTCMD}${AR} rcs libpiano.a ${LIBPIANO_OBJ}
-include $(PIANOBAR_SRC:.c=.d)
@@ -111,17 +119,17 @@ libpiano.so.0: ${LIBPIANO_RELOBJ} ${LIBPIANO_HDR} ${LIBPIANO_OBJ}
# build standard object files
%.o: %.c
- @echo " CC $<"
- @${CC} -c -o $@ ${ALL_CFLAGS} -MMD -MF $*.d -MP $<
+ ${SILENTECHO} " CC $<"
+ ${SILENTCMD}${CC} -c -o $@ ${ALL_CFLAGS} -MMD -MF $*.d -MP $<
# create position independent code (for shared libraries)
%.lo: %.c
- @echo " CC $< (PIC)"
- @${CC} -c -fPIC -o $@ ${ALL_CFLAGS} -MMD -MF $*.d -MP $<
+ ${SILENTECHO} " CC $< (PIC)"
+ ${SILENTCMD}${CC} -c -fPIC -o $@ ${ALL_CFLAGS} -MMD -MF $*.d -MP $<
clean:
- @echo " CLEAN"
- @${RM} ${PIANOBAR_OBJ} ${LIBPIANO_OBJ} \
+ ${SILENTECHO} " CLEAN"
+ ${SILENTCMD}${RM} ${PIANOBAR_OBJ} ${LIBPIANO_OBJ} \
${LIBPIANO_RELOBJ} pianobar libpiano.so* \
libpiano.a $(PIANOBAR_SRC:.c=.d) $(LIBPIANO_SRC:.c=.d)
@@ -146,4 +154,13 @@ install-libpiano:
install -d ${DESTDIR}${INCDIR}/
install -m644 src/libpiano/piano.h ${DESTDIR}${INCDIR}/
-.PHONY: install install-libpiano test debug all
+uninstall:
+ $(RM) ${DESTDIR}/${BINDIR}/pianobar \
+ ${DESTDIR}/${MANDIR}/man1/pianobar.1 \
+ ${DESTDIR}/${LIBDIR}/libpiano.so.0.0.0 \
+ ${DESTDIR}/${LIBDIR}/libpiano.so.0 \
+ ${DESTDIR}/${LIBDIR}/libpiano.so \
+ ${DESTDIR}/${LIBDIR}/libpiano.a \
+ ${DESTDIR}/${INCDIR}/piano.h
+
+.PHONY: install install-libpiano uninstall test debug all
diff --git a/README.md b/README.md
index 7d1afcd..20b2638 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,15 @@
-#pianobar
-
pianobar is a console client for the personalized web radio [Pandora]
-([http://www.pandora.com](http://www.pandora.com)).
+([http://www.pandora.com](http://www.pandora.com)) ported to Windows.
+
+![pianobar](https://github.com/thedmd/pianobar-windows/blob/feature/appveyor/screenshots/pianobar.png)
+
+# Releases
-###Features
+Releases can be found at [GitHub Release page](https://github.com/thedmd/pianobar-windows/releases).
+
+[![Build status](https://ci.appveyor.com/api/projects/status/6n5qa9bs7aiy8e52?svg=true)](https://ci.appveyor.com/project/thedmd/pianobar-windows)
+
+### Features
* Play and manage (create, add more music, delete, rename, ...) your stations.
* Rate played songs and let pandora explain why they have been selected.
@@ -12,11 +18,14 @@ pianobar is a console client for the personalized web radio [Pandora]
* last.fm scrobbling support (external application)
* Proxy support for listeners outside the USA.
-###Source Code
+### Source Code
-The source code can be downloaded at [github.com](http://github.com/PromyLOPh/pianobar/)
+Original source code can be downloaded at [github.com](http://github.com/PromyLOPh/pianobar/)
or [6xq.net](http://6xq.net/projects/pianobar/).
-###Download/Installation
+### Building
+
+Checkout [pianobar-windows-build](https://github.com/thedmd/pianobar-windows-build) where
+you will find configured solution for Visual Studio 2015.
-There are community provided packages available for most Linux distributions (see your distribution’s package manager), Mac OS X ([MacPorts](http://trac.macports.org/browser/trunk/dports/audio/pianobar/Portfile) or [homebrew](http://brew.sh/)) and *BSD as well as a [native Windows port](https://github.com/thedmd/pianobar-windows).
+This repository is linked by GitHub submodule.
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..c4f645a
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,79 @@
+version: '{branch}-{build}'
+configuration: Release
+platform:
+- x86
+- x64
+clone_script:
+- ps: >-
+ # Clone build environment
+
+ git clone -q --branch=master https://github.com/thedmd/pianobar-windows-build.git $env:appveyor_build_folder
+
+
+ # Clone project itself
+
+ if(-not $env:appveyor_pull_request_number) {
+ git clone -q --branch=$env:appveyor_repo_branch https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder\pianobar\src
+ cd $env:appveyor_build_folder\pianobar\src; git checkout -qf $env:appveyor_repo_commit
+ } else {
+ git clone -q https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder\pianobar\src
+ cd $env:appveyor_build_folder\pianobar\src; git fetch -q origin +refs/pull/$env:appveyor_pull_request_number/merge:
+ cd $env:appveyor_build_folder\pianobar\src; git checkout -qf FETCH_HEAD
+ }
+build:
+ verbosity: minimal
+after_build:
+- ps: >-
+ $artifactName = "pianobar"
+
+ if([System.Convert]::ToBoolean($env:appveyor_repo_tag))
+
+ {
+ $artifactName = "$artifactName-$env:appveyor_repo_tag_name"
+ }
+
+ else
+
+ {
+ $branchName = $env:appveyor_build_version -replace "/", "-"
+ $artifactName = "$artifactName-$branchName"
+ }
+
+
+ function Package
+
+ {
+ [cmdletbinding()]
+ Param([string]$BinaryDir, [string]$ArtifactName, [string]$OutputDir, [string]$Suffix)
+ Process
+ {
+ New-Item -ItemType directory $OutputDir\release-$Suffix
+ Copy-Item $env:appveyor_build_folder\pianobar\src\release\* $OutputDir\release-$Suffix
+ Copy-Item $BinaryDir\*.exe $OutputDir\release-$Suffix
+ 7z a $OutputDir\$ArtifactName-$Suffix.zip $OutputDir\release-$Suffix\*
+ }
+ }
+
+
+ if(Test-Path -Path $env:appveyor_build_folder\build\Win32)
+
+ {
+ Package -BinaryDir $env:appveyor_build_folder\build\Win32 -ArtifactName $artifactName -Suffix x86 -OutputDir $env:appveyor_build_folder\build
+ }
+
+
+ if(Test-Path -Path $env:appveyor_build_folder\build\x64)
+
+ {
+ Package -BinaryDir $env:appveyor_build_folder\build\x64 -ArtifactName $artifactName -Suffix x64 -OutputDir $env:appveyor_build_folder\build
+ }
+artifacts:
+- path: build\*.zip
+deploy:
+- provider: GitHub
+ auth_token:
+ secure: bXlXe4mzmi9lpGSfWMvWf01I05hyCuYZAVrlM5ZUad86QfyYvO6EeKTPaCpbjdyp
+ draft: true
+ force_update: true
+ on:
+ branch: /\d\d\d\d\.\d\d.\d\d.*/ \ No newline at end of file
diff --git a/contrib/config-example b/contrib/config-example
index 899ee39..0a69526 100644
--- a/contrib/config-example
+++ b/contrib/config-example
@@ -10,6 +10,7 @@
# Proxy (for those who are not living in the USA)
#control_proxy = http://127.0.0.1:9090/
+#bind_to = if!tun0
# Keybindings
#act_help = ?
@@ -49,6 +50,7 @@
#ban_icon = [-]
#volume = 0
#ca_bundle = /etc/ssl/certs/ca-certificates.crt
+#gain_mul = 1.0
# Format strings
#format_nowplaying_song = %t by %a on %l%r%@%s
diff --git a/contrib/eventcmd-examples/eventcmd.sh b/contrib/eventcmd-examples/eventcmd.sh
index 7e2872a..233b52e 100755
--- a/contrib/eventcmd-examples/eventcmd.sh
+++ b/contrib/eventcmd-examples/eventcmd.sh
@@ -11,7 +11,7 @@ case "$1" in
# songstart)
# echo 'naughty.notify({title = "pianobar", text = "Now playing: ' "$title" ' by ' "$artist" '"})' | awesome-client -
-# echo "$title -- $artist" > $HOME/.config/pianobar/nowplaying
+# echo "$title -- $artist" > "${XDG_HOME_CONFIG:-${HOME}/.config}/pianobar/nowplaying"
# if [ "$rating" -eq 1 ]
# then
diff --git a/contrib/pianobar.1 b/contrib/pianobar.1
index ee09c41..bf385f6 100644
--- a/contrib/pianobar.1
+++ b/contrib/pianobar.1
@@ -200,9 +200,20 @@ or the key you defined in
Icon for banned songs.
.TP
-.B ca_bundle
+.B bind_to = {if!tunX,host!x.x.x.x,..}
+This sets the interface name to use as outgoing network interface. The name can
+be an interface name, an IP address, or a host name. (from CURLOPT_INTERFACE)
+
+It can be used as a replacement for
+.B control_proxy
+in conjunction with OpenVPN's
+option
+.B route-nopull.
+
+.TP
+.B ca_bundle = /etc/ssl/certs/ca-certificates.crt
Path to CA certifiate bundle, containing the root and intermediate certificates
-required to validate Pandora’s SSL certificate.
+required to validate Pandora's SSL certificate.
.TP
.B control_proxy = http://user:password@host:port/
@@ -299,6 +310,12 @@ Station name
Station id
.TP
+.B gain_mul = 1.0
+Pandora sends a ReplayGain value with every song. This sets a multiplier so that the gain adjustment can be
+reduced. 0.0 means no gain adjustment, 1.0 means full gain adjustment, values inbetween reduce the magnitude
+of gain adjustment.
+
+.TP
.B history = 5
Keep a history of the last n songs (5, by default). You can rate these songs.
@@ -344,10 +361,6 @@ sorts by name from a to z, quickmix_01_name_za by type (quickmix at the
bottom) and name from z to a.
.TP
-.B tls_fingerprint = D9980BA2CC0F97BB03822C6211EAEA4A06EEF427
-Hex-encoded SHA1 fingerprint of Pandora's TLS certificate.
-
-.TP
.B user = your@user.name
Your pandora.com username.
diff --git a/pianobar.cfg.example b/pianobar.cfg.example
new file mode 100644
index 0000000..ed86e88
--- /dev/null
+++ b/pianobar.cfg.example
@@ -0,0 +1,31 @@
+# Uncomment the control_proxy line if you would like to set up a proxy connection for pianobar
+#control_proxy = http://<proxy_user>:<proxy_pass>@<proxy_address>:<proxy_port>
+
+user = <pandora_user>
+password = <pandora_password>
+
+#width = 220
+#height = 60
+
+#-------------------------------------------------------------------------------
+# Uncomment these if you're using GlobalPandora.com
+
+#rpc_host = internal-tuner.pandora.com
+#partner_user = pandora one
+#partner_password = TVCKIBGS9AO9TSYLNNFUML0743LH82D
+#device = D01
+#encrypt_password = 2%3WCL*JU$MP]4
+#decrypt_password = U#IO$RZPAB%VX2
+#tls_fingerprint = B0A1EB460B1B6F33A1B6CB500C6523CB2E6EC946
+
+
+# Messages with colors using terminal escape codes
+format_nowplaying_song = "%t" by "%a" on "%l"%r%@%s
+format_nowplaying_station = Station "%n" (%i)
+format_list_song = %i) %a - %t%r
+format_msg_info = (i) %s
+format_msg_nowplaying = |> %s
+format_msg_time = # %s
+format_msg_err = /!\ %s
+format_msg_question = [?] %s
+format_msg_debug = %s \ No newline at end of file
diff --git a/release/README.md b/release/README.md
new file mode 100644
index 0000000..f98315b
--- /dev/null
+++ b/release/README.md
@@ -0,0 +1,34 @@
+pianobar for Windows - portable binaries
+========
+
+![pianobar](https://github.com/thedmd/pianobar-windows-binaries/blob/master/screenshots/pianobar.png)
+
+pianobar is a console client for the personalized web radio pandora
+(http://www.pandora.com). Source code of the original project can be found at
+at http://github.com/PromyLOPh/pianobar/ or http://6xq.net/projects/pianobar/
+
+This project contains binaries for Windows build using Microsoft
+Visual Studio 2015.
+
+json-c, vtparse are used to prepare this distributtion.
+
+Source code of this binary can be found at:
+https://github.com/thedmd/pianobar-windows-build
+
+
+CONFIGURATION
+
+Pianobar use configuration file. Under Windows this file is named pianobar.cfg
+and should be placed next to pianobar.exe.
+On reporitory there is an example configuration file, you may copy or rename it.
+Then edit it and fill marked fields relevant to you and remove remaining.
+
+Note that non-US users have to have configuration file with control proxy
+details set.
+
+
+GLOBALPANDORA.COM
+
+If you're behind proxy please look into pianobar.cfg.example and copy necessary
+settings to your own configuration file. This are necessary, because
+globalpandora.com does not support server used in by pianobar.
diff --git a/release/pianobar.cfg.example b/release/pianobar.cfg.example
new file mode 100644
index 0000000..ed86e88
--- /dev/null
+++ b/release/pianobar.cfg.example
@@ -0,0 +1,31 @@
+# Uncomment the control_proxy line if you would like to set up a proxy connection for pianobar
+#control_proxy = http://<proxy_user>:<proxy_pass>@<proxy_address>:<proxy_port>
+
+user = <pandora_user>
+password = <pandora_password>
+
+#width = 220
+#height = 60
+
+#-------------------------------------------------------------------------------
+# Uncomment these if you're using GlobalPandora.com
+
+#rpc_host = internal-tuner.pandora.com
+#partner_user = pandora one
+#partner_password = TVCKIBGS9AO9TSYLNNFUML0743LH82D
+#device = D01
+#encrypt_password = 2%3WCL*JU$MP]4
+#decrypt_password = U#IO$RZPAB%VX2
+#tls_fingerprint = B0A1EB460B1B6F33A1B6CB500C6523CB2E6EC946
+
+
+# Messages with colors using terminal escape codes
+format_nowplaying_song = "%t" by "%a" on "%l"%r%@%s
+format_nowplaying_station = Station "%n" (%i)
+format_list_song = %i) %a - %t%r
+format_msg_info = (i) %s
+format_msg_nowplaying = |> %s
+format_msg_time = # %s
+format_msg_err = /!\ %s
+format_msg_question = [?] %s
+format_msg_debug = %s \ No newline at end of file
diff --git a/screenshots/pianobar.png b/screenshots/pianobar.png
new file mode 100644
index 0000000..61096ab
--- /dev/null
+++ b/screenshots/pianobar.png
Binary files differ
diff --git a/src/config.h b/src/config.h
index c408542..42a0b11 100644
--- a/src/config.h
+++ b/src/config.h
@@ -1,10 +1,9 @@
-#ifndef SRC_CONFIG_H_S6A1C09K
-#define SRC_CONFIG_H_S6A1C09K
+# pragma once
/* package name */
#define PACKAGE "pianobar"
-#define VERSION "2015.12.10"
+#define VERSION "2017.05.18"
#define TITLE "Pianobar"
@@ -25,4 +24,3 @@
#define CURL_STATICLIB
-#endif /* SRC_CONFIG_H_S6A1C09K */
diff --git a/src/console.h b/src/console.h
index de5d951..627e77b 100644
--- a/src/console.h
+++ b/src/console.h
@@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_CONSOLE_H_WY8F3MNH
-#define SRC_CONSOLE_H_WY8F3MNH
+# pragma once
#include "config.h"
#include <stdio.h>
@@ -49,4 +48,3 @@ void BarConsolePuts(const char* c);
void BarConsolePrint(const char* format, ...);
void BarConsolePrintV(const char* format, va_list args);
-#endif /* SRC_CONSOLE_H_WY8F3MNH */
diff --git a/src/http/http.c b/src/http/http.c
index dd64ad1..4576d04 100644
--- a/src/http/http.c
+++ b/src/http/http.c
@@ -44,6 +44,7 @@ static wchar_t* HttpToWideString(const char* string, int size);
static bool HttpCreateConnection (http_t http);
static void HttpCloseConnection (http_t http);
static void HttpSetLastError (http_t http, const char* message);
+static void HttpSetLastErrorW (http_t http, const wchar_t* message);
static void HttpSetLastErrorFromWinHttp (http_t http);
static char* HttpFormatWinApiError (DWORD errorCode, HINSTANCE module);
static char* HttpFormatWinHttpError (DWORD errorCode);
@@ -118,11 +119,19 @@ static void HttpCloseConnection (http_t http) {
static void HttpSetLastError (http_t http, const char* message) {
free(http->error);
http->error = NULL;
-
+
if (message)
http->error = strdup(message);
}
+static void HttpSetLastErrorW (http_t http, const wchar_t* message) {
+ free(http->error);
+ http->error = NULL;
+
+ if (message)
+ http->error = HttpToString(message, wcslen(message));
+}
+
static void HttpSetLastErrorFromWinHttp (http_t http) {
free(http->error);
http->error = NULL;
@@ -241,6 +250,32 @@ bool HttpSetAutoProxy (http_t http, const char* url) {
return false;
}
+static void HttpUrlDecodeInplace (wchar_t* url)
+{
+ wchar_t* input = url;
+ wchar_t* output = url;
+ size_t size = wcslen (url);
+ while (size > 0) {
+ if (input[0] == '%' && iswxdigit(input[1]) && iswxdigit(input[2])) {
+ wchar_t hex[3];
+ hex[0] = input[1];
+ hex[1] = input[2];
+ hex[2] = 0;
+ *output++ = (wchar_t)(wcstol (hex, NULL, 16));
+ input += 3;
+ size -= 3;
+ }
+ else {
+ *output++ = *input++;
+ --size;
+ }
+ }
+
+ if (output < input) {
+ *output = '\0';
+ }
+}
+
bool HttpSetProxy (http_t http, const char* url) {
URL_COMPONENTS urlComponents;
wchar_t* wideUrl = NULL;
@@ -258,10 +293,12 @@ bool HttpSetProxy (http_t http, const char* url) {
if (urlComponents.lpszUserName && urlComponents.dwUserNameLength > 0) {
wideUsername = wcsdup(urlComponents.lpszUserName);
wideUsername[urlComponents.dwUserNameLength] = 0;
+ HttpUrlDecodeInplace (wideUsername);
}
if (urlComponents.lpszPassword && urlComponents.dwPasswordLength > 0) {
widePassword = wcsdup(urlComponents.lpszPassword);
widePassword[urlComponents.dwPasswordLength] = 0;
+ HttpUrlDecodeInplace (widePassword);
}
}
@@ -396,6 +433,13 @@ bool HttpRequest(http_t http, PianoRequest_t * const request) {
}
if (succeeded && statusCode == 407) {
+ wchar_t statusText[256] = { 0 };
+ DWORD statusTextSize = sizeof(statusText) - 1;
+ WinHttpQueryHeaders(handle,
+ WINHTTP_QUERY_STATUS_TEXT,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ statusText, &statusTextSize, WINHTTP_NO_HEADER_INDEX);
+ HttpSetLastErrorW (http, statusText);
requestSent = false;
retry = true;
}
@@ -467,6 +511,9 @@ bool HttpRequest(http_t http, PianoRequest_t * const request) {
HttpSetLastError (http, "Maximum retries count exceeded");
}
+ if (retryLimit == 0)
+ goto done;
+
complete = true;
HttpSetLastError (http, NULL);
diff --git a/src/http/http.h b/src/http/http.h
index a321cd3..5ea617f 100644
--- a/src/http/http.h
+++ b/src/http/http.h
@@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_HTTP_H_CN979RE9
-#define SRC_HTTP_H_CN979RE9
+#pragma once
#include "config.h"
@@ -42,5 +41,3 @@ bool HttpSetProxy(http_t, const char*);
bool HttpRequest (http_t, PianoRequest_t * const);
const char* HttpGetError (http_t);
-#endif /* SRC_HTTP_H_CN979RE9 */
-
diff --git a/src/libpiano/crypt.h b/src/libpiano/crypt.h
index 890b8c1..30d2294 100644
--- a/src/libpiano/crypt.h
+++ b/src/libpiano/crypt.h
@@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_LIBPIANO_CRYPT_H_O832IVGK
-#define SRC_LIBPIANO_CRYPT_H_O832IVGK
+#pragma once
#include "piano.h"
@@ -34,4 +33,3 @@ char *PianoDecryptString (PianoCipher_t, const char * const,
size_t * const);
char *PianoEncryptString (PianoCipher_t, const char *);
-#endif /* SRC_LIBPIANO_CRYPT_H_O832IVGK */
diff --git a/src/libpiano/piano.h b/src/libpiano/piano.h
index ca33626..2dbf607 100644
--- a/src/libpiano/piano.h
+++ b/src/libpiano/piano.h
@@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_LIBPIANO_PIANO_H_MFBT13PN
-#define SRC_LIBPIANO_PIANO_H_MFBT13PN
+#pragma once
#include "../config.h"
@@ -366,4 +365,3 @@ PianoStation_t *PianoFindStationById (PianoStation_t * const,
const char * const);
const char *PianoErrorToStr (PianoReturn_t);
-#endif /* SRC_LIBPIANO_PIANO_H_MFBT13PN */
diff --git a/src/libpiano/piano_private.h b/src/libpiano/piano_private.h
index bc0cc0e..ffc14c8 100644
--- a/src/libpiano/piano_private.h
+++ b/src/libpiano/piano_private.h
@@ -21,12 +21,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_LIBPIANO_PIANO_PRIVATE_H_C35CDGGL
-#define SRC_LIBPIANO_PIANO_PRIVATE_H_C35CDGGL
+#pragma once
#include "piano.h"
void PianoDestroyStation (PianoStation_t *station);
void PianoDestroyUserInfo (PianoUserInfo_t *user);
-#endif /* SRC_LIBPIANO_PIANO_PRIVATE_H_C35CDGGL */
diff --git a/src/libpiano/response.c b/src/libpiano/response.c
index 8b6ed52..e37824e 100644
--- a/src/libpiano/response.c
+++ b/src/libpiano/response.c
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2008-2013
+Copyright (c) 2008-2017
Lars-Dominik Braun <lars@6xq.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -34,16 +34,34 @@ THE SOFTWARE.
#include "crypt.h"
static char *PianoJsonStrdup (json_object *j, const char *key) {
- return strdup (json_object_get_string (json_object_object_get (j, key)));
+ assert (j != NULL);
+ assert (key != NULL);
+
+ json_object *v;
+ if (json_object_object_get_ex (j, key, &v)) {
+ return strdup (json_object_get_string (v));
+ } else {
+ return NULL;
+ }
+}
+
+static bool getBoolDefault (json_object * const j, const char * const key, const bool def) {
+ assert (j != NULL);
+ assert (key != NULL);
+
+ json_object *v;
+ if (json_object_object_get_ex (j, key, &v)) {
+ return json_object_get_boolean (v);
+ } else {
+ return def;
+ }
}
static void PianoJsonParseStation (json_object *j, PianoStation_t *s) {
s->name = PianoJsonStrdup (j, "stationName");
s->id = PianoJsonStrdup (j, "stationToken");
- s->isCreator = !json_object_get_boolean (json_object_object_get (j,
- "isShared"));
- s->isQuickMix = json_object_get_boolean (json_object_object_get (j,
- "isQuickMix"));
+ s->isCreator = !getBoolDefault (j, "isShared", !false);
+ s->isQuickMix = getBoolDefault (j, "isQuickMix", false);
}
/* concat strings
@@ -77,23 +95,22 @@ static void PianoStrpcat (char * restrict dest, const char * restrict src,
*/
PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
PianoReturn_t ret = PIANO_RET_OK;
- json_object *j, *result, *status;
assert (ph != NULL);
assert (req != NULL);
- j = json_tokener_parse (req->responseData);
+ json_object * const j = json_tokener_parse (req->responseData);
- status = json_object_object_get (j, "stat");
- if (status == NULL) {
- json_object_put (j);
- return PIANO_RET_INVALID_RESPONSE;
+ json_object *status;
+ if (!json_object_object_get_ex (j, "stat", &status)) {
+ ret = PIANO_RET_INVALID_RESPONSE;
+ goto cleanup;
}
/* error handling */
if (strcmp (json_object_get_string (status), "ok") != 0) {
- json_object *code = json_object_object_get (j, "code");
- if (code == NULL) {
+ json_object *code;
+ if (!json_object_object_get_ex (j, "code", &code)) {
ret = PIANO_RET_INVALID_RESPONSE;
} else {
ret = json_object_get_int (code)+PIANO_RET_OFFSET;
@@ -110,11 +127,12 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
}
}
- json_object_put (j);
- return ret;
+ goto cleanup;
}
- result = json_object_object_get (j, "result");
+ json_object *result = NULL;
+ /* missing for some request types */
+ json_object_object_get_ex (j, "result", &result);
switch (req->type) {
case PIANO_REQUEST_LOGIN: {
@@ -127,8 +145,14 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
switch (reqData->step) {
case 0: {
/* decrypt timestamp */
- const char * const cryptedTimestamp = json_object_get_string (
- json_object_object_get (result, "syncTime"));
+ json_object *jsonTimestamp;
+ if (!json_object_object_get_ex (result, "syncTime", &jsonTimestamp)) {
+ ret = PIANO_RET_INVALID_RESPONSE;
+ break;
+ }
+ assert (jsonTimestamp != NULL);
+ const char * const cryptedTimestamp = json_object_get_string (jsonTimestamp);
+ assert (cryptedTimestamp != NULL);
const time_t realTimestamp = time (NULL);
char *decryptedTimestamp = NULL;
size_t decryptedSize;
@@ -148,8 +172,12 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
/* get auth token */
ph->partner.authToken = PianoJsonStrdup (result,
"partnerAuthToken");
- ph->partner.id = json_object_get_int (
- json_object_object_get (result, "partnerId"));
+ json_object *partnerId;
+ if (!json_object_object_get_ex (result, "partnerId", &partnerId)) {
+ ret = PIANO_RET_INVALID_RESPONSE;
+ break;
+ }
+ ph->partner.id = json_object_get_int (partnerId);
++reqData->step;
break;
}
@@ -172,8 +200,11 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
/* get stations */
assert (req->responseData != NULL);
- json_object *stations = json_object_object_get (result,
- "stations"), *mix = NULL;
+ json_object *stations, *mix = NULL;
+
+ if (!json_object_object_get_ex (result, "stations", &stations)) {
+ break;
+ }
for (int i = 0; i < json_object_array_length (stations); i++) {
PianoStation_t *tmpStation;
@@ -187,7 +218,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
if (tmpStation->isQuickMix) {
/* fix flags on other stations later */
- mix = json_object_object_get (s, "quickMixStationIds");
+ json_object_object_get_ex (s, "quickMixStationIds", &mix);
}
/* start new linked list or append */
@@ -219,7 +250,10 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
assert (reqData != NULL);
assert (reqData->quality != PIANO_AQ_UNKNOWN);
- json_object *items = json_object_object_get (result, "items");
+ json_object *items = NULL;
+ if (!json_object_object_get_ex (result, "items", &items)) {
+ break;
+ }
assert (items != NULL);
for (int i = 0; i < json_object_array_length (items); i++) {
@@ -230,7 +264,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
return PIANO_RET_OUT_OF_MEMORY;
}
- if (json_object_object_get (s, "artistName") == NULL) {
+ if (!json_object_object_get_ex (s, "artistName", NULL)) {
free (song);
continue;
}
@@ -240,15 +274,15 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
"highQuality"};
assert (reqData->quality < sizeof (qualityMap)/sizeof (*qualityMap));
static const char *formatMap[] = {"", "aacplus", "mp3"};
- json_object *map = json_object_object_get (s, "audioUrlMap");
- assert (map != NULL);
- if (map != NULL) {
- map = json_object_object_get (map, qualityMap[reqData->quality]);
-
- if (map != NULL) {
- const char *encoding = json_object_get_string (
- json_object_object_get (map, "encoding"));
+ json_object *umap;
+ if (json_object_object_get_ex (s, "audioUrlMap", &umap)) {
+ assert (umap != NULL);
+ json_object *jsonEncoding, *qmap;
+ if (json_object_object_get_ex (umap, qualityMap[reqData->quality], &qmap) &&
+ json_object_object_get_ex (qmap, "encoding", &jsonEncoding)) {
+ assert (qmap != NULL);
+ const char *encoding = json_object_get_string (jsonEncoding);
assert (encoding != NULL);
for (size_t k = 0; k < sizeof (formatMap)/sizeof (*formatMap); k++) {
if (strcmp (formatMap[k], encoding) == 0) {
@@ -256,7 +290,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
break;
}
}
- song->audioUrl = PianoJsonStrdup (map, "audioUrl");
+ song->audioUrl = PianoJsonStrdup (qmap, "audioUrl");
} else {
/* requested quality is not available */
ret = PIANO_RET_QUALITY_UNAVAILABLE;
@@ -266,6 +300,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
}
}
+ json_object *v;
song->artist = PianoJsonStrdup (s, "artistName");
song->album = PianoJsonStrdup (s, "albumName");
song->title = PianoJsonStrdup (s, "songName");
@@ -273,12 +308,12 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
song->stationId = PianoJsonStrdup (s, "stationId");
song->coverArt = PianoJsonStrdup (s, "albumArtUrl");
song->detailUrl = PianoJsonStrdup (s, "songDetailUrl");
- song->fileGain = (float)json_object_get_double (
- json_object_object_get (s, "trackGain"));
- song->length = json_object_get_int (
- json_object_object_get (s, "trackLength"));
- switch (json_object_get_int (json_object_object_get (s,
- "songRating"))) {
+ song->fileGain = json_object_object_get_ex (s, "trackGain", &v) ?
+ (float)json_object_get_double (v) : 0.0f;
+ song->length = json_object_object_get_ex (s, "trackLength", &v) ?
+ json_object_get_int (v) : 0;
+ switch (json_object_object_get_ex (s, "songRating", &v) ?
+ json_object_get_int (v) : 0) {
case 1:
song->rating = PIANO_RATE_LOVE;
break;
@@ -340,8 +375,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
memset (searchResult, 0, sizeof (*searchResult));
/* get artists */
- json_object *artists = json_object_object_get (result, "artists");
- if (artists != NULL) {
+ json_object *artists;
+ if (json_object_object_get_ex (result, "artists", &artists)) {
for (int i = 0; i < json_object_array_length (artists); i++) {
json_object *a = json_object_array_get_idx (artists, i);
PianoArtist_t *artist;
@@ -359,8 +394,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
}
/* get songs */
- json_object *songs = json_object_object_get (result, "songs");
- if (songs != NULL) {
+ json_object *songs;
+ if (json_object_object_get_ex (result, "songs", &songs)) {
for (int i = 0; i < json_object_array_length (songs); i++) {
json_object *s = json_object_array_get_idx (songs, i);
PianoSong_t *song;
@@ -414,8 +449,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
case PIANO_REQUEST_GET_GENRE_STATIONS: {
/* get genre stations */
- json_object *categories = json_object_object_get (result, "categories");
- if (categories != NULL) {
+ json_object *categories;
+ if (json_object_object_get_ex (result, "categories", &categories)) {
for (int i = 0; i < json_object_array_length (categories); i++) {
json_object *c = json_object_array_get_idx (categories, i);
PianoGenreCategory_t *tmpGenreCategory;
@@ -429,9 +464,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
"categoryName");
/* get genre subnodes */
- json_object *stations = json_object_object_get (c,
- "stations");
- if (stations != NULL) {
+ json_object *stations;
+ if (json_object_object_get_ex (c, "stations", &stations)) {
for (int k = 0;
k < json_object_array_length (stations); k++) {
json_object *s =
@@ -480,9 +514,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
assert (reqData != NULL);
- json_object *explanations = json_object_object_get (result,
- "explanations");
- if (explanations != NULL) {
+ json_object *explanations;
+ if (json_object_object_get_ex (result, "explanations", &explanations)) {
reqData->retExplain = malloc (strSize *
sizeof (*reqData->retExplain));
strncpy (reqData->retExplain, "We're playing this track "
@@ -490,9 +523,11 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
for (int i = 0; i < json_object_array_length (explanations); i++) {
json_object *e = json_object_array_get_idx (explanations,
i);
- const char *s = json_object_get_string (
- json_object_object_get (e, "focusTraitName"));
-
+ json_object *f;
+ if (!json_object_object_get_ex (e, "focusTraitName", &f)) {
+ continue;
+ }
+ const char *s = json_object_get_string (f);
PianoStrpcat (reqData->retExplain, s, strSize);
if (i < json_object_array_length (explanations)-2) {
PianoStrpcat (reqData->retExplain, ", ", strSize);
@@ -511,8 +546,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
assert (settings != NULL);
- settings->explicitContentFilter = json_object_get_boolean (
- json_object_object_get (result, "isExplicitContentFilterEnabled"));
+ settings->explicitContentFilter = getBoolDefault (result,
+ "isExplicitContentFilterEnabled", false);
settings->username = PianoJsonStrdup (result, "username");
break;
}
@@ -528,11 +563,11 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
assert (info != NULL);
/* parse music seeds */
- json_object *music = json_object_object_get (result, "music");
- if (music != NULL) {
+ json_object *music;
+ if (json_object_object_get_ex (result, "music", &music)) {
/* songs */
- json_object *songs = json_object_object_get (music, "songs");
- if (songs != NULL) {
+ json_object *songs;
+ if (json_object_object_get_ex (music, "songs", &songs)) {
for (int i = 0; i < json_object_array_length (songs); i++) {
json_object *s = json_object_array_get_idx (songs, i);
PianoSong_t *seedSong;
@@ -552,9 +587,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
}
/* artists */
- json_object *artists = json_object_object_get (music,
- "artists");
- if (artists != NULL) {
+ json_object *artists;
+ if (json_object_object_get_ex (music, "artists", &artists)) {
for (int i = 0; i < json_object_array_length (artists); i++) {
json_object *a = json_object_array_get_idx (artists, i);
PianoArtist_t *seedArtist;
@@ -574,14 +608,12 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
}
/* parse feedback */
- json_object *feedback = json_object_object_get (result,
- "feedback");
- if (feedback != NULL) {
+ json_object *feedback;
+ if (json_object_object_get_ex (result, "feedback", &feedback)) {
static const char * const keys[] = {"thumbsUp", "thumbsDown"};
for (size_t i = 0; i < sizeof (keys)/sizeof (*keys); i++) {
- json_object * const val = json_object_object_get (feedback,
- keys[i]);
- if (val == NULL) {
+ json_object *val;
+ if (!json_object_object_get_ex (feedback, keys[i], &val)) {
continue;
}
assert (json_object_is_type (val, json_type_array));
@@ -599,9 +631,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) {
"artistName");
feedbackSong->feedbackId = PianoJsonStrdup (s,
"feedbackId");
- feedbackSong->rating = json_object_get_boolean (
- json_object_object_get (s, "isPositive")) ?
- PIANO_RATE_LOVE : PIANO_RATE_BAN;
+ feedbackSong->rating = getBoolDefault (s, "isPositive",
+ false) ? PIANO_RATE_LOVE : PIANO_RATE_BAN;
info->feedback = PianoListAppendP (info->feedback,
feedbackSong);
diff --git a/src/main.c b/src/main.c
index f0172da..4f4214a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,6 +1,6 @@
/*
Copyright (c) 2008-2013
- Lars-Dominik Braun <lars@6xq.net>
+ Lars-Dominik Braun <lars@6xq.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -31,120 +31,133 @@ THE SOFTWARE.
#include "ui.h"
#include "ui_dispatch.h"
#include "ui_readline.h"
+#include "settings.h"
/* authenticate user
*/
-static bool BarMainLoginUser (BarApp_t *app) {
- PianoReturn_t pRet;
- PianoRequestDataLogin_t reqData;
- bool ret;
-
- reqData.user = app->settings.username;
- reqData.password = app->settings.password;
- reqData.step = 0;
-
- BarUiMsg (&app->settings, MSG_INFO, "Login... ");
- ret = BarUiPianoCall (app, PIANO_REQUEST_LOGIN, &reqData, &pRet);
- BarUiStartEventCmd (&app->settings, "userlogin", NULL, NULL, &app->player,
- NULL, pRet);
-
- return ret;
+static bool BarMainLoginUser(BarApp_t *app)
+{
+ PianoReturn_t pRet;
+ PianoRequestDataLogin_t reqData;
+ bool ret;
+
+ reqData.user = app->settings.username;
+ reqData.password = app->settings.password;
+ reqData.step = 0;
+
+ BarUiMsg(&app->settings, MSG_INFO, "Login... ");
+ ret = BarUiPianoCall(app, PIANO_REQUEST_LOGIN, &reqData, &pRet);
+ BarUiStartEventCmd(&app->settings, "userlogin", NULL, NULL, &app->player,
+ NULL, pRet);
+
+ return ret;
}
/* ask for username/password if none were provided in settings
*/
-static bool BarMainGetLoginCredentials (BarSettings_t *settings,
- BarReadline_t rl) {
- bool usernameFromConfig = true;
+static bool BarMainGetLoginCredentials(BarSettings_t *settings,
+ BarReadline_t rl)
+{
+ bool usernameFromConfig = true;
- if (settings->username == NULL) {
- char nameBuf[100];
+ if (settings->username == NULL)
+ {
+ char nameBuf[100];
- BarUiMsg (settings, MSG_QUESTION, "Email: ");
- BarReadlineStr (nameBuf, sizeof (nameBuf), rl, BAR_RL_DEFAULT);
- settings->username = strdup (nameBuf);
- usernameFromConfig = false;
- }
+ BarUiMsg(settings, MSG_QUESTION, "Email: ");
+ if (BarReadlineStr(nameBuf, sizeof(nameBuf), rl, BAR_RL_DEFAULT) == 0)
+ return false;
+ settings->username = strdup(nameBuf);
+ usernameFromConfig = false;
+ }
- if (settings->password == NULL) {
- char passBuf[100];
+ if (settings->password == NULL)
+ {
+ char passBuf[100];
- if (usernameFromConfig) {
- BarUiMsg (settings, MSG_QUESTION, "Email: %s\n", settings->username);
- }
+ if (usernameFromConfig)
+ {
+ BarUiMsg(settings, MSG_QUESTION, "Email: %s\n", settings->username);
+ }
if (settings->passwordCmd == NULL) {
BarUiMsg (settings, MSG_QUESTION, "Password: ");
- BarReadlineStr (passBuf, sizeof (passBuf), rl, BAR_RL_NOECHO);
- /* write missing newline */
- BarConsolePuts("");
+ if (BarReadlineStr (passBuf, sizeof (passBuf), rl, BAR_RL_NOECHO) == 0) {
+ BarConsolePutc('\n');
+ return false;
+ }
+ /* write missing newline */
+ BarConsolePutc('\n');
settings->password = strdup (passBuf);
- } else {
+ }
+ else
+ {
//pid_t chld;
//int pipeFd[2];
- //BarUiMsg (settings, MSG_INFO, "Requesting password from external helper... ");
-
- //if (pipe (pipeFd) == -1) {
- // BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
- // return false;
- //}
-
- //chld = fork ();
- //if (chld == 0) {
- // /* child */
- // close (pipeFd[0]);
- // dup2 (pipeFd[1], fileno (stdout));
- // execl ("/bin/sh", "/bin/sh", "-c", settings->passwordCmd, (char *) NULL);
- // BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
- // close (pipeFd[1]);
- // exit (1);
- //} else if (chld == -1) {
- // BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
- // return false;
- //} else {
- // /* parent */
- // int status;
-
- // close (pipeFd[1]);
- // memset (passBuf, 0, sizeof (passBuf));
- // read (pipeFd[0], passBuf, sizeof (passBuf)-1);
- // close (pipeFd[0]);
-
- // /* drop trailing newlines */
- // ssize_t len = strlen (passBuf)-1;
- // while (len >= 0 && passBuf[len] == '\n') {
- // passBuf[len] = '\0';
- // --len;
- // }
-
- // waitpid (chld, &status, 0);
- // if (WEXITSTATUS (status) == 0) {
- // settings->password = strdup (passBuf);
- // BarUiMsg (settings, MSG_NONE, "Ok.\n");
- // } else {
- // BarUiMsg (settings, MSG_NONE, "Error: Exit status %i.\n", WEXITSTATUS (status));
- // return false;
- // }
- //}
- return false;
- } /* end else passwordCmd */
- }
+ //BarUiMsg (settings, MSG_INFO, "Requesting password from external helper... ");
+
+ //if (pipe (pipeFd) == -1) {
+ // BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
+ // return false;
+ //}
+
+ //chld = fork ();
+ //if (chld == 0) {
+ // /* child */
+ // close (pipeFd[0]);
+ // dup2 (pipeFd[1], fileno (stdout));
+ // execl ("/bin/sh", "/bin/sh", "-c", settings->passwordCmd, (char *) NULL);
+ // BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
+ // close (pipeFd[1]);
+ // exit (1);
+ //} else if (chld == -1) {
+ // BarUiMsg (settings, MSG_NONE, "Error: %s\n", strerror (errno));
+ // return false;
+ //} else {
+ // /* parent */
+ // int status;
+
+ // close (pipeFd[1]);
+ // memset (passBuf, 0, sizeof (passBuf));
+ // read (pipeFd[0], passBuf, sizeof (passBuf)-1);
+ // close (pipeFd[0]);
+
+ // /* drop trailing newlines */
+ // ssize_t len = strlen (passBuf)-1;
+ // while (len >= 0 && passBuf[len] == '\n') {
+ // passBuf[len] = '\0';
+ // --len;
+ // }
+
+ // waitpid (chld, &status, 0);
+ // if (WEXITSTATUS (status) == 0) {
+ // settings->password = strdup (passBuf);
+ // BarUiMsg (settings, MSG_NONE, "Ok.\n");
+ // } else {
+ // BarUiMsg (settings, MSG_NONE, "Error: Exit status %i.\n", WEXITSTATUS (status));
+ // return false;
+ // }
+ //}
+ return false;
+ } /* end else passwordCmd */
+ }
- return true;
+ return true;
}
/* get station list
*/
-static bool BarMainGetStations (BarApp_t *app) {
- PianoReturn_t pRet;
- bool ret;
-
- BarUiMsg (&app->settings, MSG_INFO, "Get stations... ");
- ret = BarUiPianoCall (app, PIANO_REQUEST_GET_STATIONS, NULL, &pRet);
- BarUiStartEventCmd (&app->settings, "usergetstations", NULL, NULL, &app->player,
- app->ph.stations, pRet);
- return ret;
+static bool BarMainGetStations(BarApp_t *app)
+{
+ PianoReturn_t pRet;
+ bool ret;
+
+ BarUiMsg(&app->settings, MSG_INFO, "Get stations... ");
+ ret = BarUiPianoCall(app, PIANO_REQUEST_GET_STATIONS, NULL, &pRet);
+ BarUiStartEventCmd(&app->settings, "usergetstations", NULL, NULL, &app->player,
+ app->ph.stations, pRet);
+ return ret;
}
/* get initial station from autostart setting or user rl
@@ -152,32 +165,31 @@ static bool BarMainGetStations (BarApp_t *app) {
static void BarMainGetInitialStation (BarApp_t *app) {
/* try to get autostart station */
if (app->settings.autostartStation != NULL) {
- app->curStation = PianoFindStationById (app->ph.stations,
+ app->nextStation = PianoFindStationById (app->ph.stations,
app->settings.autostartStation);
- if (app->curStation == NULL) {
+ if (app->nextStation == NULL) {
BarUiMsg (&app->settings, MSG_ERR,
"Error: Autostart station not found.\n");
}
}
/* no autostart? ask the user */
- if (app->curStation == NULL) {
- app->curStation = BarUiSelectStation (app, app->ph.stations,
+ if (app->nextStation == NULL) {
+ app->nextStation = BarUiSelectStation (app, app->ph.stations,
"Select station: ", NULL, app->settings.autoselect);
}
- if (app->curStation != NULL) {
- BarUiPrintStation (&app->settings, app->curStation);
- }
}
/* wait for user rl
*/
-static void BarMainHandleUserInput (BarApp_t *app) {
- char buf[2];
- if (BarReadline (buf, sizeof (buf), NULL, app->rl,
- BAR_RL_FULLRETURN | BAR_RL_NOECHO, 1) > 0) {
- BarUiDispatch (app, buf[0], app->curStation, app->playlist, true,
- BAR_DC_GLOBAL);
- }
+static void BarMainHandleUserInput(BarApp_t *app)
+{
+ char buf[2];
+ if (BarReadline(buf, sizeof(buf), NULL, app->rl,
+ BAR_RL_FULLRETURN | BAR_RL_NOECHO, 1) > 0)
+ {
+ BarUiDispatch(app, buf[0], app->curStation, app->playlist, true,
+ BAR_DC_GLOBAL);
+ }
}
/* fetch new playlist
@@ -185,20 +197,21 @@ static void BarMainHandleUserInput (BarApp_t *app) {
static void BarMainGetPlaylist (BarApp_t *app) {
PianoReturn_t pRet;
PianoRequestDataGetPlaylist_t reqData;
- reqData.station = app->curStation;
+ reqData.station = app->nextStation;
reqData.quality = app->settings.audioQuality;
BarUiMsg (&app->settings, MSG_INFO, "Receiving new playlist... ");
if (!BarUiPianoCall (app, PIANO_REQUEST_GET_PLAYLIST,
&reqData, &pRet)) {
- app->curStation = NULL;
+ app->nextStation = NULL;
} else {
app->playlist = reqData.retPlaylist;
if (app->playlist == NULL) {
BarUiMsg (&app->settings, MSG_INFO, "No tracks left.\n");
- app->curStation = NULL;
+ app->nextStation = NULL;
}
}
+ app->curStation = app->nextStation;
BarUiStartEventCmd (&app->settings, "stationfetchplaylist",
app->curStation, app->playlist, &app->player, app->ph.stations,
pRet);
@@ -206,141 +219,167 @@ static void BarMainGetPlaylist (BarApp_t *app) {
/* start new player thread
*/
-static void BarMainStartPlayback (BarApp_t *app) {
- assert (app != NULL);
+static void BarMainStartPlayback(BarApp_t *app)
+{
+ assert(app != NULL);
- const PianoSong_t * const curSong = app->playlist;
- assert (curSong != NULL);
+ const PianoSong_t * const curSong = app->playlist;
+ assert(curSong != NULL);
- BarUiPrintSong (&app->settings, curSong, app->curStation->isQuickMix ?
- PianoFindStationById (app->ph.stations,
- curSong->stationId) : NULL);
+ BarUiPrintSong(&app->settings, curSong, app->curStation->isQuickMix ?
+ PianoFindStationById(app->ph.stations,
+ curSong->stationId) : NULL);
- static const char httpPrefix[] = "http://";
- /* avoid playing local files */
- if (curSong->audioUrl == NULL ||
- strncmp (curSong->audioUrl, httpPrefix, strlen (httpPrefix)) != 0) {
- BarUiMsg (&app->settings, MSG_ERR, "Invalid song url.\n");
- } else {
- BarPlayer2SetGain(app->player, curSong->fileGain);
- BarPlayer2Open(app->player, curSong->audioUrl);
-
- /* throw event */
- BarUiStartEventCmd (&app->settings, "songstart",
- app->curStation, curSong, &app->player, app->ph.stations,
- PIANO_RET_OK);
-
- if (!BarPlayer2Play(app->player))
- ++app->playerErrors;
- else
- app->playerErrors = 0;
- }
+ static const char httpPrefix[] = "http://";
+ /* avoid playing local files */
+ if (curSong->audioUrl == NULL ||
+ strncmp(curSong->audioUrl, httpPrefix, strlen(httpPrefix)) != 0)
+ {
+ BarUiMsg(&app->settings, MSG_ERR, "Invalid song url.\n");
+ }
+ else
+ {
+ BarPlayer2SetGain(app->player, curSong->fileGain * app->settings.gainMul);
+ BarPlayer2Open(app->player, curSong->audioUrl);
+
+ /* throw event */
+ BarUiStartEventCmd(&app->settings, "songstart",
+ app->curStation, curSong, &app->player, app->ph.stations,
+ PIANO_RET_OK);
+
+ if (!BarPlayer2Play(app->player))
+ ++app->playerErrors;
+ else
+ app->playerErrors = 0;
+ }
}
/* player is done, clean up
*/
-static void BarMainPlayerCleanup (BarApp_t *app) {
- BarUiStartEventCmd (&app->settings, "songfinish", app->curStation,
- app->playlist, &app->player, app->ph.stations, PIANO_RET_OK);
+static void BarMainPlayerCleanup(BarApp_t *app)
+{
+ BarUiStartEventCmd(&app->settings, "songfinish", app->curStation,
+ app->playlist, &app->player, app->ph.stations, PIANO_RET_OK);
- BarPlayer2Finish(app->player);
+ BarPlayer2Finish(app->player);
- BarConsoleSetTitle (TITLE);
+ BarConsoleSetTitle(TITLE);
- if (app->playerErrors >= app->settings.maxPlayerErrors) {
- /* don't continue playback if thread reports too many error */
- app->curStation = NULL;
- app->playerErrors = 0;
- }
+ if (app->playerErrors >= app->settings.maxPlayerErrors)
+ {
+ /* don't continue playback if thread reports too many error */
+ app->nextStation = NULL;
+ app->playerErrors = 0;
+ }
}
/* print song duration
*/
-static void BarMainPrintTime (BarApp_t *app) {
- double songPlayed, songDuration, songRemaining;
- char sign;
+static void BarMainPrintTime(BarApp_t *app)
+{
+ double songPlayed, songDuration, songRemaining;
+ char sign;
- songDuration = BarPlayer2GetDuration(app->player);
- songPlayed = BarPlayer2GetTime(app->player);
+ songDuration = BarPlayer2GetDuration(app->player);
+ songPlayed = BarPlayer2GetTime(app->player);
- if (songPlayed <= songDuration) {
- songRemaining = songDuration - songPlayed;
- sign = '-';
- } else {
- /* longer than expected */
- songRemaining = songPlayed - songDuration;
- sign = '+';
- }
- BarUiMsg (&app->settings, MSG_TIME, "%c%02u:%02u/%02u:%02u\r",
- sign, (int)songRemaining / 60, (int)songRemaining % 60,
- (int)songDuration / 60, (int)songDuration % 60);
+ if (songPlayed <= songDuration)
+ {
+ songRemaining = songDuration - songPlayed;
+ sign = '-';
+ }
+ else
+ {
+ /* longer than expected */
+ songRemaining = songPlayed - songDuration;
+ sign = '+';
+ }
+ BarUiMsg(&app->settings, MSG_TIME, "%c%02u:%02u/%02u:%02u\r",
+ sign, (int)songRemaining / 60, (int)songRemaining % 60,
+ (int)songDuration / 60, (int)songDuration % 60);
}
/* main loop
*/
-static void BarMainLoop (BarApp_t *app) {
- if (!BarMainGetLoginCredentials (&app->settings, app->rl)) {
- return;
- }
-
- if (!BarMainLoginUser (app)) {
- return;
- }
+static void BarMainLoop(BarApp_t *app)
+{
+ if (!BarMainGetLoginCredentials(&app->settings, app->rl))
+ {
+ return;
+ }
- if (!BarMainGetStations (app)) {
- return;
- }
+ if (!BarMainLoginUser(app))
+ {
+ return;
+ }
- BarMainGetInitialStation (app);
+ if (!BarMainGetStations(app))
+ {
+ return;
+ }
- while (!app->doQuit) {
- /* song finished playing, clean up things/scrobble song */
- if (BarPlayer2IsStopped(app->player)) {
- BarMainPlayerCleanup (app);
- }
+ BarMainGetInitialStation(app);
- /* check whether player finished playing and start playing new
- * song */
- if (BarPlayer2IsFinished(app->player) && app->curStation != NULL) {
- /* what's next? */
- if (app->playlist != NULL) {
- PianoSong_t *histsong = app->playlist;
- app->playlist = PianoListNextP (app->playlist);
- histsong->head.next = NULL;
- BarUiHistoryPrepend (app, histsong);
- }
- if (app->playlist == NULL) {
+ while (!app->doQuit)
+ {
+ /* song finished playing, clean up things/scrobble song */
+ if (BarPlayer2IsStopped(app->player))
+ {
+ BarMainPlayerCleanup(app);
+ }
+
+ /* check whether player finished playing and start playing new
+ * song */
+ if (BarPlayer2IsFinished(app->player) && app->nextStation != NULL)
+ {
+ /* what's next? */
+ if (app->playlist != NULL)
+ {
+ PianoSong_t *histsong = app->playlist;
+ app->playlist = PianoListNextP(app->playlist);
+ histsong->head.next = NULL;
+ BarUiHistoryPrepend(app, histsong);
+ }
+ if (app->playlist == NULL && app->nextStation != NULL && !app->doQuit)
+ {
+ if (app->nextStation != app->curStation)
+ {
+ BarUiPrintStation (&app->settings, app->nextStation);
+ }
BarMainGetPlaylist (app);
}
- /* song ready to play */
- if (app->playlist != NULL) {
- BarMainStartPlayback (app);
- }
- }
-
- BarMainHandleUserInput (app);
-
- /* show time */
- if (BarPlayer2IsPlaying(app->player) || BarPlayer2IsPaused(app->player)) {
- BarMainPrintTime (app);
- }
- }
+ /* song ready to play */
+ if (app->playlist != NULL)
+ {
+ BarMainStartPlayback(app);
+ }
+ }
+
+ BarMainHandleUserInput(app);
+
+ /* show time */
+ if (BarPlayer2IsPlaying(app->player) || BarPlayer2IsPaused(app->player))
+ {
+ BarMainPrintTime(app);
+ }
+ }
}
-int main (int argc, char **argv) {
- static BarApp_t app;
+int main(int argc, char **argv)
+{
+ static BarApp_t app;
- memset (&app, 0, sizeof (app));
+ memset(&app, 0, sizeof(app));
- BarConsoleInit ();
+ BarConsoleInit();
- BarConsoleSetTitle (TITLE);
+ BarConsoleSetTitle(TITLE);
- /* init some things */
- BarSettingsInit (&app.settings);
- BarSettingsRead (&app.settings);
+ /* init some things */
+ BarSettingsInit(&app.settings);
+ BarSettingsRead(&app.settings);
- if (!BarPlayer2Init (&app.player, app.settings.player))
+ if (!BarPlayer2Init(&app.player, app.settings.player))
{
if (app.settings.player)
BarUiMsg(&app.settings, MSG_ERR, "Player \"%s\" initialization failed.", app.settings.player);
@@ -349,47 +388,51 @@ int main (int argc, char **argv) {
return 0;
}
- PianoReturn_t pret;
- if ((pret = PianoInit (&app.ph, app.settings.partnerUser,
- app.settings.partnerPassword, app.settings.device,
- app.settings.inkey, app.settings.outkey)) != PIANO_RET_OK) {
- BarUiMsg (&app.settings, MSG_ERR, "Initialization failed:"
- " %s\n", PianoErrorToStr (pret));
- return 0;
- }
+ PianoReturn_t pret;
+ if ((pret = PianoInit(&app.ph, app.settings.partnerUser,
+ app.settings.partnerPassword, app.settings.device,
+ app.settings.inkey, app.settings.outkey)) != PIANO_RET_OK)
+ {
+ BarUiMsg(&app.settings, MSG_ERR, "Initialization failed:"
+ " %s\n", PianoErrorToStr(pret));
+ return 0;
+ }
- BarUiMsg (&app.settings, MSG_NONE,
- "Welcome to " PACKAGE " (" VERSION ")!\n");
- if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED) {
- BarUiMsg (&app.settings, MSG_NONE, "\n");
- } else {
- BarUiMsg (&app.settings, MSG_NONE,
- "Press %c for a list of commands.\n",
- app.settings.keys[BAR_KS_HELP]);
- }
+ BarUiMsg(&app.settings, MSG_NONE,
+ "Welcome to " PACKAGE " (" VERSION ")!\n");
+ if (app.settings.keys[BAR_KS_HELP] == BAR_KS_DISABLED)
+ {
+ BarUiMsg(&app.settings, MSG_NONE, "\n");
+ }
+ else
+ {
+ BarUiMsg(&app.settings, MSG_NONE,
+ "Press %c for a list of commands.\n",
+ app.settings.keys[BAR_KS_HELP]);
+ }
- HttpInit(&app.http2, app.settings.rpcHost, app.settings.rpcTlsPort);
- if (app.settings.controlProxy)
- HttpSetProxy(app.http2, app.settings.controlProxy);
+ HttpInit(&app.http2, app.settings.rpcHost, app.settings.rpcTlsPort);
+ if (app.settings.controlProxy)
+ HttpSetProxy(app.http2, app.settings.controlProxy);
- BarReadlineInit (&app.rl);
+ BarReadlineInit(&app.rl);
- BarMainLoop (&app);
+ BarMainLoop(&app);
- BarReadlineDestroy (app.rl);
+ BarReadlineDestroy(app.rl);
- /* write statefile */
- BarSettingsWrite (app.curStation, &app.settings);
+ /* write statefile */
+ BarSettingsWrite(app.curStation, &app.settings);
- PianoDestroy (&app.ph);
- PianoDestroyPlaylist (app.songHistory);
- PianoDestroyPlaylist (app.playlist);
- HttpDestroy (app.http2);
- BarPlayer2Destroy (app.player);
- BarSettingsDestroy (&app.settings);
- BarConsoleDestroy ();
+ PianoDestroy(&app.ph);
+ PianoDestroyPlaylist(app.songHistory);
+ PianoDestroyPlaylist(app.playlist);
+ HttpDestroy(app.http2);
+ BarPlayer2Destroy(app.player);
+ BarSettingsDestroy(&app.settings);
+ BarConsoleDestroy();
- return 0;
+ return 0;
}
diff --git a/src/main.h b/src/main.h
index 6c34d71..192ffdb 100644
--- a/src/main.h
+++ b/src/main.h
@@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_MAIN_H_4ZGSCG6X
-#define SRC_MAIN_H_4ZGSCG6X
+# pragma once
//#include <curl/curl.h>
@@ -42,11 +41,11 @@ typedef struct {
/* first item is current song */
PianoSong_t *playlist;
PianoSong_t *songHistory;
- PianoStation_t *curStation;
+ /* station of current song and station used to fetch songs from if playlist
+ * is empty */
+ PianoStation_t *curStation, *nextStation;
char doQuit;
BarReadline_t rl;
unsigned int playerErrors;
} BarApp_t;
-#endif /* SRC_MAIN_H_4ZGSCG6X */
-
diff --git a/src/player/backends/media_foundation.h b/src/player/backends/media_foundation.h
index cf10122..6ad4966 100644
--- a/src/player/backends/media_foundation.h
+++ b/src/player/backends/media_foundation.h
@@ -1,5 +1,3 @@
-# ifndef __TD__BASIC_MEDIA_PLAYER_H__
-# define __TD__BASIC_MEDIA_PLAYER_H__
# pragma once
# include <mfapi.h>
@@ -98,7 +96,7 @@ private:
com_ptr<IMFSimpleAudioVolume> m_SimpleAudioVolume;
com_ptr<IMFPresentationClock> m_PresentationClock;
com_ptr<IMFAudioStreamVolume> m_StreamAudioVolume;
-
+
optional<float> m_SetMasterVolume;
mutable float m_MasterVolume;
mutable float m_ReplayGain;
@@ -110,6 +108,3 @@ private:
State m_State;
HANDLE m_CloseEvent;
};
-
-
-# endif // __TD__BASIC_MEDIA_PLAYER_H__ \ No newline at end of file
diff --git a/src/player/backends/utility/com_ptr.h b/src/player/backends/utility/com_ptr.h
index 23534b2..519caef 100644
--- a/src/player/backends/utility/com_ptr.h
+++ b/src/player/backends/utility/com_ptr.h
@@ -1,5 +1,3 @@
-# ifndef __TD__COM_PTR_H__
-# define __TD__COM_PTR_H__
# pragma once
template <typename T>
@@ -144,5 +142,3 @@ template <typename T> inline bool operator<=(const com_ptr<T>& a, const com_ptr<
template <typename T> inline bool operator>(const com_ptr<T>& a, const com_ptr<T>& b) { return std::greater<T*>()(a.get(), b.get()); }
template <typename T> inline bool operator>=(const com_ptr<T>& a, const com_ptr<T>& b) { return std::greater_equal<T*>()(a.get(), b.get()); }
template <typename T> void swap(com_ptr<T> & lhs, com_ptr<T> & rhs) { lhs.swap(rhs); }
-
-# endif // __TD__COM_PTR_H__ \ No newline at end of file
diff --git a/src/player/backends/utility/optional.h b/src/player/backends/utility/optional.h
index 9ee0b71..dcdc59a 100644
--- a/src/player/backends/utility/optional.h
+++ b/src/player/backends/utility/optional.h
@@ -1,8 +1,6 @@
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
-# ifndef __TD__OPTIONAL_H__
-# define __TD__OPTIONAL_H__
# pragma once
# include <type_traits>
@@ -512,6 +510,3 @@ inline bool operator>=(const T& v, const optional<T>& opt)
using namespace std;
return static_cast<bool>(opt) ? greater_equal<T>(v, *opt) : true;
}
-
-
-# endif // __TD__OPTIONAL_H__
diff --git a/src/player/player2.h b/src/player/player2.h
index 710b35d..3ac29c2 100644
--- a/src/player/player2.h
+++ b/src/player/player2.h
@@ -21,8 +21,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef __PIANOBAR_PLAYER2_H__
-#define __PIANOBAR_PLAYER2_H__
#pragma once
#include "config.h"
@@ -49,5 +47,3 @@ bool BarPlayer2IsPaused(player2_t player);
bool BarPlayer2IsStopped(player2_t player);
bool BarPlayer2IsFinished(player2_t player);
-#endif /* __PIANOBAR_PLAYER2_H__ */
-
diff --git a/src/player/player2_private.h b/src/player/player2_private.h
index 757a07e..b76eb8b 100644
--- a/src/player/player2_private.h
+++ b/src/player/player2_private.h
@@ -1,5 +1,3 @@
-#ifndef __PIANOBAR_PLAYER2_PRIVATE_H__
-#define __PIANOBAR_PLAYER2_PRIVATE_H__
#pragma once
#include "config.h"
@@ -32,4 +30,3 @@ typedef struct _player2_iface
extern player2_iface player2_direct_show;
extern player2_iface player2_windows_media_foundation;
-#endif /* __PIANOBAR_PLAYER2_PRIVATE_H__ */
diff --git a/src/settings.c b/src/settings.c
index 0765bc8..4f0ef8c 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -27,11 +27,13 @@ THE SOFTWARE.
#include "settings.h"
#include "config.h"
+#include "ui.h"
#include "ui_dispatch.h"
#include <stdlib.h>
#include <assert.h>
#include <memory.h>
#include <string.h>
+#include <ctype.h>
#define PACKAGE_CONFIG PACKAGE ".cfg"
#define PACKAGE_STATE PACKAGE ".state"
@@ -119,6 +121,7 @@ void BarSettingsInit (BarSettings_t *settings) {
void BarSettingsDestroy (BarSettings_t *settings) {
free (settings->controlProxy);
free (settings->proxy);
+ free (settings->bindTo);
free (settings->username);
free (settings->password);
free (settings->passwordCmd);
@@ -169,6 +172,7 @@ void BarSettingsRead (BarSettings_t *settings) {
settings->autoselect = true;
settings->history = 5;
settings->volume = 0;
+ settings->gainMul = 1.0;
settings->maxPlayerErrors = 5;
settings->sortOrder = BAR_SORT_NAME_AZ;
settings->loveIcon = strdup (" <3");
@@ -180,7 +184,7 @@ void BarSettingsRead (BarSettings_t *settings) {
settings->titleFormat = strdup (TITLE " - \"%t\" by \"%a\" on \"%l\"%r%@%s");
settings->player = NULL;
settings->rpcHost = strdup (PIANO_RPC_HOST);
- settings->rpcTlsPort = NULL;
+ settings->rpcTlsPort = strdup ("443");
settings->partnerUser = strdup ("android");
settings->partnerPassword = strdup ("AC7IBG09A3DTSYM4R41UJWL07VLN8JI7");
settings->device = strdup ("android-generic");
@@ -213,8 +217,9 @@ void BarSettingsRead (BarSettings_t *settings) {
/* read config files */
for (size_t j = 0; j < sizeof (configfiles) / sizeof (*configfiles); j++) {
static const char *formatMsgPrefix = "format_msg_";
- char key[256], val[256];
FILE *configfd;
+ char line[512];
+ size_t lineNum = 0;
char * const path = BarGetXdgConfigDir (configfiles[j]);
assert (path != NULL);
@@ -224,18 +229,70 @@ void BarSettingsRead (BarSettings_t *settings) {
}
while (1) {
- char lwhite, rwhite;
- int scanRet = fscanf (configfd, "%255s%c=%c%255[^\n]", key, &lwhite, &rwhite, val);
- if (scanRet == EOF) {
+ ++lineNum;
+ char * const ret = fgets (line, sizeof (line), configfd);
+ if (ret == NULL) {
+ /* EOF or error */
break;
- } else if (scanRet != 4 || lwhite != ' ' || rwhite != ' ') {
- /* invalid config line */
+ }
+ if (strchr (line, '\n') == NULL && !feof (configfd)) {
+ BarUiMsg (settings, MSG_INFO, "Line %s:%zu too long, "
+ "ignoring\n", path, lineNum);
+ continue;
+ }
+ /* parse lines that match "^\s*(.*?)\s?=\s?(.*)$". Windows and Unix
+ * line terminators are supported. */
+ char *key = line;
+
+ /* skip leading spaces */
+ while (isspace ((unsigned char) key[0])) {
+ ++key;
+ }
+
+ /* skip comments */
+ if (key[0] == '#') {
continue;
}
+
+ /* search for delimiter and split key-value pair */
+ char *val = strchr (line, '=');
+ if (val == NULL) {
+ /* no warning for empty lines */
+ if (key[0] != '\0') {
+ BarUiMsg (settings, MSG_INFO,
+ "Invalid line at %s:%zu\n", path, lineNum);
+ }
+ /* invalid line */
+ continue;
+ }
+ *val = '\0';
+ ++val;
+
+ /* drop spaces at the end */
+ char *keyend = &key[strlen (key)-1];
+ while (keyend >= key && isspace ((unsigned char) *keyend)) {
+ *keyend = '\0';
+ --keyend;
+ }
+
+ /* strip at most one space, legacy cruft, required for values with
+ * leading spaces like love_icon */
+ if (isspace ((unsigned char) val[0])) {
+ ++val;
+ }
+ /* drop trailing cr/lf */
+ char *valend = &val[strlen (val)-1];
+ while (valend >= val && (*valend == '\r' || *valend == '\n')) {
+ *valend = '\0';
+ --valend;
+ }
+
if (streq ("control_proxy", key)) {
settings->controlProxy = strdup (val);
} else if (streq ("proxy", key)) {
settings->proxy = strdup (val);
+ } else if (streq ("bind_to", key)) {
+ settings->bindTo = strdup (val);
} else if (streq ("user", key)) {
settings->username = strdup (val);
} else if (streq ("password", key)) {
@@ -322,6 +379,8 @@ void BarSettingsRead (BarSettings_t *settings) {
settings->atIcon = strdup (val);
} else if (streq ("volume", key)) {
settings->volume = atoi (val);
+ } else if (streq ("gain_mul", key)) {
+ settings->gainMul = (float)atof (val);
} else if (streq ("format_nowplaying_song", key)) {
free (settings->npSongFormat);
settings->npSongFormat = strdup (val);
diff --git a/src/settings.h b/src/settings.h
index 78d0e45..0edb544 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_SETTINGS_H_IPL0ON9L
-#define SRC_SETTINGS_H_IPL0ON9L
+#pragma once
#include <stdbool.h>
@@ -83,16 +82,20 @@ typedef struct {
char *postfix;
} BarMsgFormatStr_t;
+#include "ui_types.h"
+
typedef struct {
bool autoselect;
unsigned int history, maxPlayerErrors;
int volume;
+ float gainMul;
BarStationSorting_t sortOrder;
PianoAudioQuality_t audioQuality;
char *username;
char *password, *passwordCmd;
char *controlProxy; /* non-american listeners need this */
char *proxy;
+ char *bindTo;
char *autostartStation;
char *eventCmd;
char *loveIcon;
@@ -114,4 +117,3 @@ void BarSettingsDestroy (BarSettings_t *);
void BarSettingsRead (BarSettings_t *);
void BarSettingsWrite (PianoStation_t *, BarSettings_t *);
-#endif /* SRC_SETTINGS_H_IPL0ON9L */
diff --git a/src/ui.c b/src/ui.c
index 8efc7aa..88ad4d7 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -715,7 +715,8 @@ void BarUiStartEventCmd (const BarSettings_t *settings, const char *type,
// pipeWriteFd = fdopen (pipeFd[1], "w");
- // if (curSong != NULL && stations != NULL && curStation->isQuickMix) {
+ // if (curSong != NULL && stations != NULL && curStation != NULL &&
+ // curStation->isQuickMix) {
// songStation = PianoFindStationById (stations, curSong->stationId);
// }
diff --git a/src/ui.h b/src/ui.h
index 705bbab..126f6bb 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_UI_H_46P20TS0
-#define SRC_UI_H_46P20TS0
+#pragma once
#include <stdbool.h>
@@ -54,4 +53,3 @@ int BarUiPianoCall (BarApp_t * const, PianoRequestType_t,
void *, PianoReturn_t *);
void BarUiHistoryPrepend (BarApp_t *app, PianoSong_t *song);
-#endif /* SRC_UI_H_46P20TS0 */
diff --git a/src/ui_act.c b/src/ui_act.c
index 4206a33..a036f19 100644
--- a/src/ui_act.c
+++ b/src/ui_act.c
@@ -224,11 +224,17 @@ BarUiActCallback(BarUiActDeleteStation) {
if (BarUiActDefaultPianoCall (PIANO_REQUEST_DELETE_STATION,
selStation) && selStation == app->curStation) {
BarUiDoSkipSong (app->player);
- PianoDestroyPlaylist (PianoListNextP (app->playlist));
- app->playlist->head.next = NULL;
- BarUiHistoryPrepend (app, app->playlist);
- app->playlist = NULL;
+ if (app->playlist != NULL) {
+ /* drain playlist */
+ PianoDestroyPlaylist (PianoListNextP (app->playlist));
+ app->playlist->head.next = NULL;
+ selSong = NULL;
+ }
+ app->nextStation = NULL;
+ /* XXX: usually we shoudn’t touch cur*, but DELETE_STATION destroys
+ * station struct */
app->curStation = NULL;
+ selStation = NULL;
}
BarUiActDefaultEventcmd ("stationdelete");
}
@@ -446,14 +452,12 @@ BarUiActCallback(BarUiActSelectStation) {
PianoStation_t *newStation = BarUiSelectStation (app, app->ph.stations,
"Select station: ", NULL, app->settings.autoselect);
if (newStation != NULL) {
- app->curStation = newStation;
- BarUiPrintStation (&app->settings, app->curStation);
+ app->nextStation = newStation;
BarUiDoSkipSong (app->player);
if (app->playlist != NULL) {
+ /* drain playlist */
PianoDestroyPlaylist (PianoListNextP (app->playlist));
app->playlist->head.next = NULL;
- BarUiHistoryPrepend (app, app->playlist);
- app->playlist = NULL;
}
}
}
diff --git a/src/ui_act.h b/src/ui_act.h
index 676fbb4..fb4457b 100644
--- a/src/ui_act.h
+++ b/src/ui_act.h
@@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_UI_ACT_H_1FEFTC06
-#define SRC_UI_ACT_H_1FEFTC06
+#pragma once
#include <piano.h>
@@ -63,4 +62,3 @@ BarUiActCallback(BarUiActManageStation);
BarUiActCallback(BarUiActVolReset);
BarUiActCallback(BarUiActSettings);
-#endif /* SRC_UI_ACT_H_1FEFTC06 */
diff --git a/src/ui_dispatch.h b/src/ui_dispatch.h
index 24b68bb..7e34393 100644
--- a/src/ui_dispatch.h
+++ b/src/ui_dispatch.h
@@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_UI_DISPATCH_H_PV2JON1Z
-#define SRC_UI_DISPATCH_H_PV2JON1Z
+#pragma once
/* bit-mask */
typedef enum {
@@ -114,5 +113,3 @@ static const BarUiDispatchAction_t dispatchActions[BAR_KS_COUNT] = {
BarKeyShortcutId_t BarUiDispatch (BarApp_t *, const char, PianoStation_t *, PianoSong_t *,
const bool, BarUiDispatchContext_t);
-#endif /* SRC_UI_DISPATCH_H_PV2JON1Z */
-
diff --git a/src/ui_readline.h b/src/ui_readline.h
index 9343e1a..83214b9 100644
--- a/src/ui_readline.h
+++ b/src/ui_readline.h
@@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_UI_READLINE_H_IFRX74VM
-#define SRC_UI_READLINE_H_IFRX74VM
+#pragma once
#include "config.h"
@@ -46,5 +45,3 @@ size_t BarReadlineStr (char *, const size_t,
size_t BarReadlineInt (int *, BarReadline_t);
bool BarReadlineYesNo (bool, BarReadline_t);
-#endif /* SRC_UI_READLINE_H_IFRX74VM */
-
diff --git a/src/ui_types.h b/src/ui_types.h
index aab4199..ecd32d4 100644
--- a/src/ui_types.h
+++ b/src/ui_types.h
@@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef SRC_UI_TYPES_H_2HR75RII
-#define SRC_UI_TYPES_H_2HR75RII
+# pragma once
typedef enum {
MSG_NONE = 0,
@@ -36,4 +35,3 @@ typedef enum {
MSG_COUNT = 8, /* invalid type */
} BarUiMsg_t;
-#endif /* SRC_UI_TYPES_H_2HR75RII */