diff options
34 files changed, 790 insertions, 434 deletions
@@ -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 @@ -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 = [32m%t[0m by [34m%a[0m on %l[31m%r[0m%@%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 = "[92m%t[0m" by "[96m%a[0m" on "[93m%l[0m"[91m%r[0m%@%s +format_nowplaying_station = Station "[95m%n[0m" [90m(%i)[0m +format_list_song = %i) %a - [92m%t[0m%r +format_msg_info = [97m(i) [0m%s +format_msg_nowplaying = [36m|>[0m %s +format_msg_time = [90m# [97m%s[0m +format_msg_err = [90m/!\[0m %s +format_msg_question = [97m[?][0m %s +format_msg_debug = [90m%s[0m
\ 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 = "[92m%t[0m" by "[96m%a[0m" on "[93m%l[0m"[91m%r[0m%@%s +format_nowplaying_station = Station "[95m%n[0m" [90m(%i)[0m +format_list_song = %i) %a - [92m%t[0m%r +format_msg_info = [97m(i) [0m%s +format_msg_nowplaying = [36m|>[0m %s +format_msg_time = [90m# [97m%s[0m +format_msg_err = [90m/!\[0m %s +format_msg_question = [97m[?][0m %s +format_msg_debug = [90m%s[0m
\ No newline at end of file diff --git a/screenshots/pianobar.png b/screenshots/pianobar.png Binary files differnew file mode 100644 index 0000000..61096ab --- /dev/null +++ b/screenshots/pianobar.png 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); @@ -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; } @@ -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 */ @@ -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); // } @@ -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 */ |