diff --git a/.dep.inc b/.dep.inc deleted file mode 100644 index 38ba445..0000000 --- a/.dep.inc +++ /dev/null @@ -1,5 +0,0 @@ -# This code depends on make tool being used -DEPFILES=$(wildcard $(addsuffix .d, ${OBJECTFILES} ${TESTOBJECTFILES})) -ifneq (${DEPFILES},) -include ${DEPFILES} -endif diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..dd88d9b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true + +[*.{cpp,h}] +indent_style = tab +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true + diff --git a/.gitignore b/.gitignore index f233604..8a1ebce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -/dist/* -/objects.*/ +dist/* +objects*/ StreamRadio-version.rdef diff --git a/About.cpp b/About.cpp index ae6734d..3223927 100644 --- a/About.cpp +++ b/About.cpp @@ -12,19 +12,12 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ -/* - * File: About.cpp - * Author: Kai Niessen - * - * Created on March 20, 2017, 4:11 PM - */ #include "About.h" -#include "RadioApp.h" -#include "Utils.h" + #include #include #include @@ -35,6 +28,10 @@ #include #include +#include "RadioApp.h" +#include "Utils.h" + + #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "About" @@ -52,16 +49,19 @@ About::About() versionView->SetViewColor(238, 238, 235, 255); versionView->SetHighColor(134, 135, 138, 255); versionView->SetLowColor(134, 135, 138, 0); + BBitmap* banner = Utils::ResourceBitmap(RES_BANNER); - if (banner) { + if (banner != NULL) { bounds = banner->Bounds(); ResizeTo(bounds.Width(), bounds.Height() + versionView->PreferredSize().height); + versionView->SetFontSize(11); versionView->SetAlignment(B_ALIGN_RIGHT); versionView->ResizeTo( bounds.Width(), versionView->PreferredSize().height + 2); versionView->MoveTo(0, bounds.Height()); + BView* bannerView = new BView(bounds, "bannerView", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW); AddChild(bannerView, versionView); @@ -69,9 +69,11 @@ About::About() } else { versionView->SetFontSize(40); versionView->SetAlignment(B_ALIGN_CENTER); + BSize preferredSize = versionView->PreferredSize(); ResizeTo(preferredSize.width + 10, preferredSize.height); } + CenterOnScreen(); } @@ -97,5 +99,6 @@ About::GetAppVersion() versionString.SetTo(versionInfo.long_info); else versionString = "©Fishpond 2012-2017"; + return versionString; } diff --git a/About.h b/About.h index a324539..2d4b931 100644 --- a/About.h +++ b/About.h @@ -12,32 +12,27 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ +#ifndef _ABOUT_H +#define _ABOUT_H -/* - * File: About.h - * Author: Kai Niessen - * - * Created on March 20, 2017, 4:11 PM - */ - -#ifndef ABOUT_H -#define ABOUT_H #include #include + #define BANNER 4 -class About : public BWindow -{ + +class About : public BWindow { public: - About(); - virtual ~About(); + About(); + virtual ~About(); private: - static BString GetAppVersion(); + static BString GetAppVersion(); }; -#endif /* ABOUT_H */ + +#endif // _ABOUT_H diff --git a/Buffering.h b/Buffering.h index cdc61d5..74fe880 100644 --- a/Buffering.h +++ b/Buffering.h @@ -1,38 +1,56 @@ /* - * File: Buffering.h - * Author: Kai Niessen + * Copyright (C) 2017 Kai Niessen * - * Created on January 31, 2016, 4:15 PM + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ +#ifndef _BUFFERING_H +#define _BUFFERING_H -#ifndef BUFFERING_H -#define BUFFERING_H /** * Simple class extending an integer to roll over when reaching boundaries */ -class rotor -{ +class rotor { public: rotor(int size, int initialValue = 0) { fMax = size - 1; fValue = initialValue; } + + rotor(rotor& other) { fMax = other.fMax; fValue = other.fValue; } + + operator int() { return fValue; } + + rotor& operator++() { if (fValue == fMax) fValue = 0; else fValue++; + return *this; } + + rotor& operator++(int) { rotor* tmp = new rotor(*this); @@ -40,16 +58,22 @@ class rotor fValue = 0; else fValue++; + return *tmp; } + + rotor& operator--() { if (fValue == 0) fValue = fMax; else fValue--; + return *this; } + + rotor& operator--(int) { rotor* tmp = new rotor(*this); @@ -57,60 +81,122 @@ class rotor fValue = fMax; else fValue--; + return *tmp; } - rotor& operator=(int i) { fValue = i % (fMax + 1); } - int operator=(rotor& r) { return r.fValue; } + + + rotor& operator=(int i) + { + fValue = i % (fMax + 1); + } + + + int operator=(rotor& r) + { + return r.fValue; + } + + int operator-(rotor& other) { return (fValue - other.fValue + fMax + 1) % (fMax + 1); } - bool operator==(int i) { return fValue == i; } - bool operator!=(int i) { return fValue != i; } - bool operator<(int i) { return fValue < i; } - bool operator>(int i) { return fValue > i; } + + + bool operator==(int i) + { + return fValue == i; + } + + + bool operator!=(int i) + { + return fValue != i; + } + + + bool operator<(int i) + { + return fValue < i; + } + + + bool operator>(int i) + { + return fValue > i; + } + +private: + inline void _SetValue(int i) { fValue = i; } + inline int _GetValue() { return fValue; } private: - inline void setValue(int i) { fValue = i; } - inline int getValue() { return fValue; } - int fValue; - int fMax; + int fValue; + int fMax; }; -class Buffer : public BLocker -{ + +class Buffer : public BLocker { public: #define limit(requested, max) (requested = (requested > max) ? max : requested) + friend class BufferGroup; - typedef enum { EMPTY, FILLING, FILLED, READING } bufferState; + + typedef enum {EMPTY, FILLING, FILLED, READING} bufferState; + + Buffer(size_t size, int index) - : BLocker("buffer"), fIndex(index), fUsed(0), fSize(size), - fData((char*) malloc(size)), fState(EMPTY) + : + BLocker("Buffer"), + fIndex(index), + fUsed(0), + fSize(size), + fData((char*)malloc(size)), + fState(EMPTY) + {}; + + ~Buffer() { + free(fData); } - ~Buffer() { free(fData); } + + // Caller handles locking! - inline size_t fillable() { return fSize - fUsed; } - inline char* fillPos() { return fData + fUsed; } - inline bool isFull() { return fUsed == fSize; } - inline bufferState state() { return fState; } - void setState(bufferState state) + inline size_t fillable() { return fSize - fUsed; } + inline char* fillPos() { return fData + fUsed; } + inline bool isFull() { return fUsed == fSize; } + + + inline bufferState state() { return fState; } + + void + setState(bufferState state) { if (state == EMPTY) fUsed = 0; fState = state; } - inline char* data() { return fData; } - inline size_t readable() { return fUsed; } - size_t fill(char* data, size_t size) + + + inline char* data() { return fData; } + inline size_t readable() { return fUsed; } + + + size_t + fill(char* data, size_t size) { if (limit(size, fillable())) { memcpy(fillPos(), data, size); fUsed += size; } + return size; } - size_t read(char* data, size_t size) + + + size_t + read(char* data, size_t size) { if (limit(size, readable())) memcpy(data, fData, size); @@ -119,124 +205,179 @@ class Buffer : public BLocker } protected: - bufferState fState; - size_t fSize; - size_t fUsed; - char* fData; - int fIndex; + bufferState fState; + size_t fSize; + size_t fUsed; + char* fData; + int fIndex; }; + + typedef Buffer* pBuffer; -class BufferGroup : public BLocker -{ + +class BufferGroup : public BLocker { public: -#define none -1 - typedef bool (*LimitFunc)(void* cookie, float ratio, bool isOk); + #define kNone -1 + + typedef bool (*LimitFunc)(void* cookie, float ratio, bool isOk); + BufferGroup(int numBuffers, size_t size) - : BLocker("buffers"), firstEmpty(numBuffers, 0), - firstFilled(numBuffers, none), fNumBuffers(numBuffers) + : + BLocker("BufferGroup"), + firstEmpty(numBuffers, 0), + firstFilled(numBuffers, kNone), + fNumBuffers(numBuffers) { - buffers = new pBuffer[numBuffers]; + fBuffers = new pBuffer[numBuffers]; for (int i = 0; i < numBuffers; i++) - buffers[i] = new Buffer(size, i); + fBuffers[i] = new Buffer(size, i); } + + ~BufferGroup() { for (int i = 0; i < fNumBuffers; i++) - delete buffers[i]; - delete buffers; + delete fBuffers[i]; + delete fBuffers; } - inline int IndexOf(Buffer* buffer) { return buffer->fIndex; } - Buffer* RequestForFilling(Buffer* previous = NULL) + + + inline int + IndexOf(Buffer* buffer) + { + return buffer->fIndex; + } + + + Buffer* + RequestForFilling(Buffer* previous = NULL) { Buffer* result = NULL; Lock(); - if (previous) { + + if (previous != NULL) { if (previous->CountLocks() == 0) previous->Lock(); previous->fState = Buffer::FILLED; - if (firstFilled == none) + if (firstFilled == kNone) firstFilled = IndexOf(previous); previous->Unlock(); } - if (firstEmpty != none) - result = buffers[firstEmpty++]; + + if (firstEmpty != kNone) + result = fBuffers[firstEmpty++]; result->fState = Buffer::FILLING; - if (buffers[firstEmpty]->fState != Buffer::EMPTY) - firstEmpty = none; + + if (fBuffers[firstEmpty]->fState != Buffer::EMPTY) + firstEmpty = kNone; + Unlock(); return result; } - Buffer* RequestForReading(Buffer* previous = NULL) + + + Buffer* + RequestForReading(Buffer* previous = NULL) { Buffer* result = NULL; Lock(); - if (previous) { + + if (previous != NULL) { if (previous->CountLocks() == 0) previous->Lock(); previous->setState(Buffer::EMPTY); - if (firstEmpty == none) + if (firstEmpty == kNone) firstEmpty = IndexOf(previous); previous->Unlock(); } - if (firstFilled != none) - result = buffers[firstFilled++]; + + if (firstFilled != kNone) + result = fBuffers[firstFilled++]; result->fState = Buffer::READING; - if (buffers[firstFilled]->fState != Buffer::FILLED) - firstFilled = none; + + if (fBuffers[firstFilled]->fState != Buffer::FILLED) + firstFilled = kNone; + Unlock(); + return result; } - void ReturnBuffer(Buffer* previous) + + + void + ReturnBuffer(Buffer* previous) { if (previous->CountLocks() == 0) previous->Lock(); + if (previous->fState == Buffer::FILLING && previous->fUsed == 0) previous->setState(Buffer::EMPTY); Lock(); + switch (previous->fState) { case Buffer::READING: case Buffer::EMPTY: + { previous->setState(Buffer::EMPTY); - if (firstEmpty == none) + if (firstEmpty == kNone) firstEmpty = IndexOf(previous); break; + } + case Buffer::FILLING: previous->fState = Buffer::FILLED; case Buffer::FILLED: - if (firstFilled == none) + { + if (firstFilled == kNone) firstFilled = previous->fIndex; break; + } } + Unlock(); previous->Unlock(); } - size_t TotalUsed() + + + size_t + TotalUsed() { size_t result = 0; for (int i = 0; i < fNumBuffers; i++) - Buffer* b = buffers[i]; + Buffer* b = fBuffers[i]; if (b->fState == Buffer::FILLED || b->fState == Buffer::FILLING) result += b->fUsed; + return result; } - size_t TotalCapacity() { return fNumBuffers * buffers[0]->fSize; } - float FillRatio() + + + size_t + TotalCapacity() + { + return fNumBuffers * fBuffers[0]->fSize; + } + + + float + FillRatio() { - if (firstFilled == none) + if (firstFilled == kNone) return 0.0f; - else if (firstEmpty == none) + else if (firstEmpty == kNone) return 1.0f; else return float(firstEmpty - firstFilled) / fNumBuffers; } private: - int fNumBuffers; - pBuffer* buffers; - rotor firstEmpty; - rotor firstFilled; + rotor fFirstFilled; + rotor fFirstEmpty; + int32 fNumBuffers; + pBuffer* fBuffers; }; -#endif /* BUFFERING_H */ + +#endif // _BUFFERING_H diff --git a/Debug.h b/Debug.h index 2e4724b..76af107 100644 --- a/Debug.h +++ b/Debug.h @@ -1,17 +1,30 @@ /* - * File: Debug.h - * Author: Kai Niessen + * Copyright (C) 2017 Kai Niessen * - * Created on January 16, 2016, 7:50 PM + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef _DEBUG_H +#define _DEBUG_H + + +/* * Contains definitions for tracing, debugging, profiling and logging * * To enable trace / debug / rpofiling messages, uncomment the associated * defines or configure them in your IDE / make file. */ -#ifndef DEBUG_H -#define DEBUG_H #undef TRACE #undef DEBUG @@ -42,4 +55,5 @@ #define PROFILE_MEASURE(action) #endif // PROFILING -#endif // DEBUG_H + +#endif // _DEBUG_H diff --git a/Expander.cpp b/Expander.cpp index 8001d91..baf0c2e 100644 --- a/Expander.cpp +++ b/Expander.cpp @@ -12,17 +12,12 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ -/* - * File: Expander.cpp - * Author: Kai Niessen - * - * Created on April 13, 2017, 4:50 PM - */ #include "Expander.h" + #include #include #include @@ -52,6 +47,7 @@ Expander::Draw(BRect updateRect) StrokeShape(&shape); shape.Clear(); + r.InsetBy(r.Width() / 3, 2); if (fExpanded) { shape.MoveTo(r.LeftTop()); @@ -64,6 +60,7 @@ Expander::Draw(BRect updateRect) shape.LineTo(r.LeftTop() + BPoint(r.Width() / 2, 0)); shape.Close(); } + SetHighColor(tint_color(ViewColor(), B_DARKEN_2_TINT)); FillShape(&shape); } @@ -98,7 +95,9 @@ Expander::SetExpanded(bool expanded) fTarget->Show(); else fTarget->Hide(); + Window()->Layout(true); + fExpanded = expanded; Invalidate(); } diff --git a/Expander.h b/Expander.h index d8a89ff..064b80f 100644 --- a/Expander.h +++ b/Expander.h @@ -12,38 +12,35 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ +#ifndef _EXPANDER_H +#define _EXPANDER_H -/* - * File: Expander.h - * Author: Kai Niessen - * - * Created on April 13, 2017, 4:50 PM - */ - -#ifndef EXPANDER_H -#define EXPANDER_H #include + #define MSG_EXPAND 'eEXP' -class Expander : public BButton -{ + +class Expander : public BButton { public: - Expander(const char* name, BView* target, - orientation orientation = B_HORIZONTAL); - virtual ~Expander(); - void SetExpanded(bool expanded); - bool Expanded() { return fExpanded; } - virtual status_t Invoke(BMessage* msg); - virtual void Draw(BRect updateRect); + Expander(const char* name, BView* target, + orientation orientation = B_HORIZONTAL); + virtual ~Expander(); + + void SetExpanded(bool expanded); + bool Expanded() { return fExpanded; } + + virtual status_t Invoke(BMessage* msg); + virtual void Draw(BRect updateRect); private: - bool fExpanded; - orientation fOrientation; - BView* fTarget; + BView* fTarget; + orientation fOrientation; + bool fExpanded; }; -#endif /* EXPANDER_H */ + +#endif // _EXPANDER_H diff --git a/HttpUtils.cpp b/HttpUtils.cpp index 40c4b2f..ba7abaf 100644 --- a/HttpUtils.cpp +++ b/HttpUtils.cpp @@ -1,38 +1,60 @@ /* - * File: HttpUtils.cpp - * Author: user + * Copyright (C) 2017 Kai Niessen * - * Created on 12. Oktober 2015, 23:14 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + #include #include "Debug.h" #include "HttpUtils.h" -class HttpIOReader : public BUrlProtocolListener -{ + +class HttpIOReader : public BUrlProtocolListener { public: - HttpIOReader(BPositionIO* data, BHttpHeaders* responseHeaders, size_t limit) - : fData(data), fLimit(limit), fHeaders(responseHeaders), + HttpIOReader(BPositionIO* data, + BHttpHeaders* responseHeaders, + ssize_t limit) + : + fData(data), + fLimit(limit), + fHeaders(responseHeaders), fIsRedirect(false) - { - } + {}; + + + ~HttpIOReader() + {}; - ~HttpIOReader() {} - virtual void HeadersReceived(BUrlRequest* caller, const BUrlResult& result) + virtual void + HeadersReceived(BUrlRequest* caller, const BUrlResult& result) { - const BHttpResult* httpResult - = dynamic_cast(&result); - if (httpResult && fHeaders) { + const BHttpResult* httpResult = + dynamic_cast(&result); + if (httpResult != NULL && fHeaders != NULL) { *fHeaders = httpResult->Headers(); fIsRedirect = BHttpRequest::IsRedirectionStatusCode( httpResult->StatusCode()); } } - virtual void DataReceived( - BUrlRequest* caller, const char* data, off_t position, ssize_t size) + + + virtual void + DataReceived(BUrlRequest* caller, const char* data, off_t position, + ssize_t size) { if (fIsRedirect) return; @@ -43,37 +65,46 @@ class HttpIOReader : public BUrlProtocolListener } fData->Write(data, size); - if (fLimit) // Was fLimit < size, which could lead to fLimit = 0 and an - // endless loop + if (fLimit != 0) { + // Was fLimit < size, which could lead to fLimit = 0 and an + // endless loop if (fLimit <= size) caller->Stop(); else fLimit -= size; + } } private: - BPositionIO* fData; - BHttpHeaders* fHeaders; - bool fIsRedirect; - - size_t fLimit; + BPositionIO* fData; + ssize_t fLimit; + BHttpHeaders* fHeaders; + bool fIsRedirect; }; -class HttpHeaderReader : public BUrlProtocolListener -{ + +class HttpHeaderReader : public BUrlProtocolListener { public: HttpHeaderReader(char* buffer = NULL, ssize_t size = 0) - : BUrlProtocolListener(), fBuffer(buffer), fSize(size), fPos(0) - { - } + : + BUrlProtocolListener(), + fBuffer(buffer), + fSize(size), + fPos(0) + {}; - virtual void HeadersReceived(BUrlRequest* caller) + + virtual void + HeadersReceived(BUrlRequest* caller) { if (fBuffer == NULL && caller->IsRunning()) caller->Stop(); } - virtual void DataReceived( - BUrlRequest* caller, const char* data, off_t position, ssize_t size) + + + virtual void + DataReceived(BUrlRequest* caller, const char* data, off_t position, + ssize_t size) { if (fSize == 0 || fBuffer == NULL) { if (caller->IsRunning()) @@ -91,11 +122,12 @@ class HttpHeaderReader : public BUrlProtocolListener } private: - char* fBuffer; - ssize_t fSize; - off_t fPos; + char* fBuffer; + ssize_t fSize; + off_t fPos; }; + /** * Loops on the DNS responses looking for connectivity on specified port. */ @@ -120,6 +152,7 @@ HttpUtils::CheckPort(BUrl url, BUrl* newUrl, uint32 flags) BNetworkAddress ipAddress; BSocket* socket; uint32 cookie = 0; + status_t portStatus = B_ERROR; while (portStatus != B_OK) { status = resolver->GetNextAddress(AF_INET6, &cookie, ipAddress); @@ -127,10 +160,11 @@ HttpUtils::CheckPort(BUrl url, BUrl* newUrl, uint32 flags) status = resolver->GetNextAddress(&cookie, ipAddress); if (status != B_OK) return status; - socket = new (std::nothrow) BSocket(); + socket = new(std::nothrow) BSocket(); portStatus = socket->Connect(ipAddress); delete socket; } + // If port number is 80, do not add it to the final URL // Then, prepend the appropiate protocol newUrlString = ipAddress.ToString(ipAddress.Port() != 80) @@ -139,6 +173,7 @@ HttpUtils::CheckPort(BUrl url, BUrl* newUrl, uint32 flags) if (url.HasPath()) newUrlString.Append(url.Path()); newUrl->SetUrlString(newUrlString.String()); + return B_OK; } @@ -155,28 +190,36 @@ HttpUtils::GetAll(BUrl url, BHttpHeaders* responseHeaders, bigtime_t timeOut, BString* contentType, size_t sizeLimit) { BMallocIO* data = new BMallocIO(); + if (data == NULL) + return data; + HttpIOReader reader(data, responseHeaders, sizeLimit); HttpRequest request(url, false, "HTTP", &reader); + if (contentType && !contentType->IsEmpty()) { BHttpHeaders* requestHeaders = new BHttpHeaders(); requestHeaders->AddHeader("accept", contentType->String()); request.AdoptHeaders(requestHeaders); } + request.SetAutoReferrer(true); request.SetFollowLocation(true); request.SetTimeout(timeOut); request.SetUserAgent("StreamRadio/0.0.4"); + thread_id threadId = request.Run(); status_t status; wait_for_thread(threadId, &status); + int32 statusCode = request.Result().StatusCode(); size_t bufferLen = data->BufferLength(); if (!(statusCode == 0 || request.IsSuccessStatusCode(statusCode)) || bufferLen == 0) { delete data; data = NULL; - } else if (contentType) + } else if (contentType != NULL) contentType->SetTo(request.Result().ContentType()); + return data; } @@ -186,6 +229,7 @@ HttpUtils::GetStreamHeader(BUrl url, BHttpHeaders* responseHeaders) { status_t status; HttpIOReader reader(NULL, responseHeaders, 0); + HttpRequest request(url, false, "HTTP", &reader); request.SetMethod("GET"); request.SetAutoReferrer(true); @@ -193,9 +237,11 @@ HttpUtils::GetStreamHeader(BUrl url, BHttpHeaders* responseHeaders) request.SetTimeout(10000); request.SetDiscardData(true); request.SetUserAgent("StreamRadio/0.0.4"); + BHttpHeaders* requestHeaders = new BHttpHeaders(); requestHeaders->AddHeader("Icy-MetaData", 1); request.AdoptHeaders(requestHeaders); + thread_id threadId = request.Run(); wait_for_thread(threadId, &status); @@ -206,5 +252,6 @@ HttpUtils::GetStreamHeader(BUrl url, BHttpHeaders* responseHeaders) else status = B_ERROR; } + return status; } diff --git a/HttpUtils.h b/HttpUtils.h index fba6027..8121ccd 100644 --- a/HttpUtils.h +++ b/HttpUtils.h @@ -1,12 +1,22 @@ /* - * File: HttpUtils.h - * Author: user + * Copyright (C) 2017 Kai Niessen * - * Created on 12. Oktober 2015, 23:14 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ +#ifndef _HTTP_UTILS_H +#define _HTTP_UTILS_H -#ifndef HTTPUTILS_H -#define HTTPUTILS_H #include #include @@ -14,28 +24,39 @@ #include #include -class HttpRequest : public BHttpRequest -{ + +class HttpRequest : public BHttpRequest { public: HttpRequest(const BUrl& url, bool ssl = false, const char* protocolName = "HTTP", BUrlProtocolListener* listener = NULL, BUrlContext* context = NULL) - : BHttpRequest(url, ssl, protocolName, listener, context){}; - virtual BHttpResult& Result() const + : + BHttpRequest(url, ssl, protocolName, listener, context) + {}; + + + virtual BHttpResult& + Result() const { - return (BHttpResult&) BHttpRequest::Result(); - }; + return (BHttpResult&)BHttpRequest::Result(); + } }; -class HttpUtils -{ + +class HttpUtils { public: - static status_t CheckPort(BUrl url, BUrl* newUrl, uint32 flags = 0); - static BMallocIO* GetAll(BUrl url, BHttpHeaders* returnHeaders = NULL, - bigtime_t timeOut = 3000, BString* contentType = NULL, - size_t sizeLimit = 0); - static status_t GetStreamHeader(BUrl url, BHttpHeaders* headers); + static status_t CheckPort(BUrl url, BUrl* newUrl, + uint32 flags = 0); + + static BMallocIO* GetAll(BUrl url, + BHttpHeaders* returnHeaders = NULL, + bigtime_t timeOut = 3000, + BString* contentType = NULL, + size_t sizeLimit = 0); + + static status_t GetStreamHeader(BUrl url, + BHttpHeaders* headers); }; -#endif /* HTTPUTILS_H */ +#endif // _HTTP_UTILS_H diff --git a/MainWindow.cpp b/MainWindow.cpp index c244783..9ab5f1e 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2010 Lukas Sommer < SommerLuk at gmail dot com > + Copyright (C) 2008-2010 Lukas Sommer < SommerLuk at gmail dot com > This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -15,18 +15,18 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . */ + #include "MainWindow.h" -#include "RadioApp.h" + #include #include #include #include #include #include -#include #include #include #include @@ -35,6 +35,8 @@ #include #include "Debug.h" +#include "RadioApp.h" + #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "MainWindow" @@ -43,20 +45,20 @@ MainWindow::MainWindow() : BWindow(BRect(0, 0, 400, 200), B_TRANSLATE_SYSTEM_NAME("StreamRadio"), - B_DOCUMENT_WINDOW, B_WILL_ACCEPT_FIRST_CLICK), + B_DOCUMENT_WINDOW, 0), fStationFinder(NULL) { - fSettings = &((RadioApp*) be_app)->Settings; + fSettings = &((RadioApp*)be_app)->Settings; - allowParallelPlayback = fSettings->GetAllowParallelPlayback(); - menuParallelPlayback = new BMenuItem(B_TRANSLATE("Allow parallel playback"), + fAllowParallelPlayback = fSettings->GetAllowParallelPlayback(); + fMenuParallelPlayback = new BMenuItem(B_TRANSLATE("Allow parallel playback"), new BMessage(MSG_PARALLEL_PLAYBACK)); - menuParallelPlayback->SetMarked(allowParallelPlayback); + fMenuParallelPlayback->SetMarked(fAllowParallelPlayback); - // initialize central widget - BLayoutBuilder::Menu<>(fMainMenu = new BMenuBar(Bounds(), "MainMenu")) + fMainMenu = new BMenuBar(Bounds(), "MainMenu"); + BLayoutBuilder::Menu<>(fMainMenu) .AddMenu(B_TRANSLATE("App")) - .AddItem(menuParallelPlayback) + .AddItem(fMenuParallelPlayback) .AddItem(B_TRANSLATE("Help" B_UTF8_ELLIPSIS), MSG_HELP) .AddItem(B_TRANSLATE("About"), B_ABOUT_REQUESTED) .AddSeparator() @@ -70,31 +72,35 @@ MainWindow::MainWindow() .AddMenu(B_TRANSLATE("Search")) .AddItem(B_TRANSLATE("Find stations" B_UTF8_ELLIPSIS), MSG_SEARCH, 'S') .End() - .End(); + .End(); AddChild(fMainMenu); + fStationList = new StationListView(true); BScrollView* stationScroll = new BScrollView( "scrollStation", fStationList, B_FOLLOW_ALL_SIDES, 0, false, true); fStationPanel = new StationPanel(this); fExpander = new Expander("expStationPanel", fStationPanel); + fStatusBar = new BStringView("status", B_EMPTY_STRING); - BGroupLayout* layout - = (BGroupLayout*) BLayoutBuilder::Group<>(this, B_VERTICAL, 0) - .SetExplicitAlignment( - BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT)) - .Add(stationScroll, 1.0f) - .Add(fExpander, 0.0f) - .Add(fStationPanel, 0.0f) - .Add(fStatusBar = new BStringView("status", ""), 0.0f); + BLayoutBuilder::Group<>(this, B_VERTICAL, 0) + .SetExplicitAlignment( + BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT)) + .Add(stationScroll, 1.0f) + .Add(fExpander, 0.0f) + .Add(fStationPanel, 0.0f) + .Add(fStatusBar, 0.0f) + .End(); fStationList->Sync(fSettings->Stations); fStationList->SetInvocationMessage(new BMessage(MSG_INVOKE_STATION)); fStationList->SetSelectionMessage(new BMessage(MSG_SELECT_STATION)); fStationList->SetPlayMessage(new BMessage(MSG_INVOKE_STATION)); + fStatusBar->SetExplicitAlignment( BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_VERTICAL_UNSET)); fStatusBar->SetExplicitMinSize(BSize(10, B_SIZE_UNSET)); + Layout(true); ResizeToPreferred(); @@ -106,10 +112,10 @@ MainWindow::MainWindow() MainWindow::~MainWindow() { - for (int i = 0; i < fStationList->CountItems(); i++) { + for (int32 i = 0; i < fStationList->CountItems(); i++) { StationListViewItem* stationItem = fStationList->ItemAt(i); StreamPlayer* player = stationItem->Player(); - if (player) + if (player != NULL) player->Stop(); } } @@ -118,13 +124,11 @@ MainWindow::~MainWindow() void MainWindow::MessageReceived(BMessage* message) { - status_t status; switch (message->what) { case B_REFS_RECEIVED: { entry_ref ref; int32 index = 0; - Station* station; StationListViewItem* stationItem; BString result; while (message->FindRef("refs", index++, &ref) == B_OK) { @@ -136,7 +140,7 @@ MainWindow::MessageReceived(BMessage* message) delete station; station = existingStation; stationItem = fStationList->Item(station); - TogglePlay(stationItem); + _TogglePlay(stationItem); } else { stationItem = new StationListViewItem(station); fStationList->AddItem(stationItem); @@ -149,6 +153,7 @@ MainWindow::MessageReceived(BMessage* message) "File %s could not be loaded as a station")); fStatusBar->SetText(result.String()); } + break; } @@ -158,6 +163,7 @@ MainWindow::MessageReceived(BMessage* message) fStationFinder = new StationFinderWindow(this); fStationFinder->Show(); } + break; } @@ -166,15 +172,17 @@ MainWindow::MessageReceived(BMessage* message) be_clipboard->Lock(); BMessage* data = be_clipboard->Data(); be_clipboard->Unlock(); + char* url; ssize_t numBytes; if (data->FindData( "text/plain", B_MIME_TYPE, (const void**) &url, &numBytes) == B_OK) url[numBytes] = 0; + BString sUrl(url); Station* station = Station::LoadIndirectUrl(sUrl); - if (station) { + if (station != NULL) { status_t probeStatus = station->Probe(); if (probeStatus == B_OK) { fSettings->Stations->AddItem(station); @@ -191,6 +199,7 @@ MainWindow::MessageReceived(BMessage* message) ->Go(); } } + break; } @@ -198,24 +207,25 @@ MainWindow::MessageReceived(BMessage* message) { StationListViewItem* stationItem = fStationList->ItemAt(fStationList->CurrentSelection(0)); - Station* station - = fStationList->StationAt(fStationList->CurrentSelection(0)); - if (stationItem) { + if (stationItem != NULL) { Station* station = stationItem->GetStation(); status_t stationStatus = station->Probe(); + BString statusText; - if (stationStatus == B_OK) + if (stationStatus == B_OK) { statusText = B_TRANSLATE("Probing station %station% successful"); - else + } else { statusText = B_TRANSLATE("Probing station %station% failed"); + } statusText.ReplaceFirst("%station%", station->Name()->String()); fStatusBar->SetText(statusText); fStationList->Invalidate(); fStationPanel->SetStation(stationItem); } + break; } @@ -223,22 +233,24 @@ MainWindow::MessageReceived(BMessage* message) { Station* station = fStationList->StationAt(fStationList->CurrentSelection(0)); - if (station) { + if (station != NULL) { fSettings->Stations->RemoveItem(station); fStationList->Sync(fSettings->Stations); fSettings->Stations->Save(); } + break; } case MSG_ADD_STATION: { Station* station = NULL; - if (B_OK == message->FindPointer("station", (void**) &station)) { + if (message->FindPointer("station", (void**) &station) == B_OK) { fSettings->Stations->AddItem(station); fStationList->Sync(fSettings->Stations); fSettings->Stations->Save(); } + break; } @@ -254,6 +266,7 @@ MainWindow::MessageReceived(BMessage* message) fStationPanel->SetStation(fStationList->ItemAt(index)); fStationPanel->UnlockLooper(); } + break; } @@ -262,30 +275,34 @@ MainWindow::MessageReceived(BMessage* message) int32 stationIndex = message->GetInt32("index", -1); StationListViewItem* stationItem = fStationList->ItemAt(stationIndex); - if (allowParallelPlayback == false) - if (activeStations.HasItem(stationItem)) - while (!activeStations.IsEmpty()) - TogglePlay(activeStations.LastItem()); - else { - while (!activeStations.IsEmpty()) - TogglePlay(activeStations.LastItem()); - TogglePlay(stationItem); + + if (fAllowParallelPlayback == false) { + if (fActiveStations.HasItem(stationItem)) { + while (!fActiveStations.IsEmpty()) + _TogglePlay(fActiveStations.LastItem()); + } else { + while (!fActiveStations.IsEmpty()) + _TogglePlay(fActiveStations.LastItem()); + + _TogglePlay(stationItem); } - else if (stationItem) - TogglePlay(stationItem); + } else if (stationItem != NULL) + _TogglePlay(stationItem); + break; } case MSG_PLAYER_STATE_CHANGED: { StreamPlayer* player = NULL; - status_t status = message->FindPointer("player", (void**) &player); + status_t status = message->FindPointer("player", (void**)&player); - if (player && status == B_OK) { + if (player != NULL && status == B_OK) { StreamPlayer::PlayState state; status = message->FindInt32("state", (int32*) &state); - int stationIndex = fStationList->StationIndex(player->GetStation()); + int stationIndex = fStationList->StationIndex( + player->GetStation()); if (stationIndex >= 0) { StationListViewItem* stationItem = fStationList->ItemAt(stationIndex); @@ -311,12 +328,13 @@ MainWindow::MessageReceived(BMessage* message) StreamPlayer* player = NULL; status_t status = message->FindPointer("player", (void**) &player); float level = message->GetFloat("level", 0.0f); - if (player && status == B_OK) { + if (player != NULL && status == B_OK) { Station* station = player->GetStation(); StationListViewItem* stationItem = fStationList->Item(station); - if (stationItem) + if (stationItem != NULL) stationItem->SetFillRatio(level); } + break; } @@ -329,22 +347,25 @@ MainWindow::MessageReceived(BMessage* message) message->GetString( "streamtitle", B_TRANSLATE("unknown title"))); fStatusBar->SetText(meta.String()); + break; } case MSG_HELP: { BUrl userguide = BUrl("/~https://github.com/HaikuArchives/" - "Haiku-Radio/blob/master/docs/userguide.md"); + "StreamRadio/blob/master/docs/userguide.md"); userguide.OpenWithPreferredApplication(true); + break; } case MSG_PARALLEL_PLAYBACK: { - allowParallelPlayback = !allowParallelPlayback; - fSettings->SetAllowParallelPlayback(allowParallelPlayback); - menuParallelPlayback->SetMarked(allowParallelPlayback); + fAllowParallelPlayback = !fAllowParallelPlayback; + fSettings->SetAllowParallelPlayback(fAllowParallelPlayback); + fMenuParallelPlayback->SetMarked(fAllowParallelPlayback); + break; } @@ -354,6 +375,7 @@ MainWindow::MessageReceived(BMessage* message) default: BWindow::MessageReceived(message); + break; } } @@ -363,6 +385,7 @@ MainWindow::QuitRequested() { fSettings->Save(); be_app->PostMessage(B_QUIT_REQUESTED); + return true; } @@ -378,16 +401,24 @@ MainWindow::SetVisible(bool visible) void -MainWindow::TogglePlay(StationListViewItem* stationItem) +MainWindow::_TogglePlay(StationListViewItem* stationItem) { - status_t status; switch (stationItem->State()) { case StreamPlayer::Stopped: { + status_t status = B_ERROR; + StreamPlayer* player = new StreamPlayer(stationItem->GetStation(), this); - status = player->InitCheck(); - if ((status == B_OK) && (status = player->Play() == B_OK)) { + if (player != NULL) { + status = player->InitCheck(); + if (status == B_OK) + status = player->Play(); + } else { + status = B_NO_MEMORY; + } + + if (status == B_OK) { stationItem->SetPlayer(player); stationItem->StateChanged(StreamPlayer::Playing); BString success; @@ -395,94 +426,37 @@ MainWindow::TogglePlay(StationListViewItem* stationItem) stationItem->GetStation()->Name()->String()); fStatusBar->SetText(success); fStatusBar->Invalidate(); - activeStations.AddItem(stationItem); + fActiveStations.AddItem(stationItem); } else { delete player; player = NULL; + BString error; error.SetToFormat(B_TRANSLATE("Failed playing station %s: %s"), stationItem->GetStation()->Name()->String(), strerror(status)); fStatusBar->SetText(error); } + break; } + case StreamPlayer::Buffering: case StreamPlayer::Playing: { StreamPlayer* player = stationItem->Player(); - if (player) + if (player != NULL) player->Stop(); stationItem->StateChanged(StreamPlayer::Stopped); fStatusBar->SetText(NULL); - activeStations.RemoveItem(stationItem); + fActiveStations.RemoveItem(stationItem); + break; } - } -} - - -void -MainWindow::DisplaySettings() -{ - return; -} - -void -MainWindow::DisplayTipOfDay() -{ - return; -} - - -void -MainWindow::SetupActions() -{ - - /* KStandardAction::preferences(this, - SLOT(display_global_settings_dialog()), actionCollection()); - - KStandardAction::quit(kapp, SLOT(quit()), actionCollection()); - - showStreamdirectoryAction = new KToggleAction(this); - showStreamdirectoryAction->setText(m_streamDirectory->windowTitle()); - QList showStreamdirectoryAction_tempShortcutList; - //showStreamdirectoryAction_tempShortcutList.append(Qt::Key_S); - //showStreamdirectoryAction_tempShortcutList.append(Qt::Key_MediaRecord); - //showStreamdirectoryAction_tempShortcutList.append(Qt::CTRL + - Qt::Key_S); - //showStreamdirectoryAction_tempShortcutList.append(Qt::META + - Qt::Key_V); - showStreamdirectoryAction->setShortcuts(showStreamdirectoryAction_tempShortcutList); - connect(showStreamdirectoryAction, SIGNAL(toggled(bool)), - this, SLOT(showStreamdirectory(bool))); - connect(m_streamDirectory, - SIGNAL(willAcceptCloseEventFromWindowSystem(bool)), - showStreamdirectoryAction, SLOT(setChecked(bool))); - actionCollection()->addAction("showStreamdirectory", - showStreamdirectoryAction); - showStreamdirectoryAction->setChecked(settings_general::showStreamdirectory()); - - KAction *importKradioripperSettingsAction = new KAction(this); - importKradioripperSettingsAction->setText( - i18nc("@action:inmenu", "Import KRadioRipper settings")); - connect(importKradioripperSettingsAction, SIGNAL(triggered(bool)), - this, SLOT(askForKradioripperImport())); - actionCollection()->addAction("importKradioripperSettings", - importKradioripperSettingsAction); - - KStandardAction::tipOfDay(this, SLOT(displayTipOfDay()), - actionCollection()); - */ - return; -} - - -void -MainWindow::AskForKradioripperImport() -{ - // importKradioripperSettings::askForImport(); - return; + case StreamPlayer::InActive: + default: + break; + } } diff --git a/MainWindow.h b/MainWindow.h index a4ef641..2af9bb3 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -15,18 +15,12 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . */ +#ifndef _MAIN_WINDOW_H +#define _MAIN_WINDOW_H -#ifndef MAINWINDOW_H -#define MAINWINDOW_H -#include "Expander.h" -#include "RadioSettings.h" -#include "StationFinder.h" -#include "StationListView.h" -#include "StationPanel.h" -#include "StreamPlayer.h" #include #include #include @@ -34,6 +28,14 @@ #include #include +#include "Expander.h" +#include "RadioSettings.h" +#include "StationFinder.h" +#include "StationListView.h" +#include "StationPanel.h" +#include "StreamPlayer.h" + + #define MSG_REFS_RECEIVED 'REFS' #define MSG_PASTE_URL 'PURL' #define MSG_SEARCH 'mSRC' @@ -43,58 +45,32 @@ #define MSG_HELP 'HELP' #define MSG_PARALLEL_PLAYBACK 'mPAR' -/** \brief The mainwindow. - * - * This class provides the mainwindow and handels the actions, session - * management, the global setting dialog, the Tip of the day and so on. - * - * \note The desctructor of this function will not necesarily be called, so also - * the children objects will not necesarrily be destroyed. So don't relay on the - * desctructor of children objects to save data, the application state and so - * on! */ -class MainWindow : public BWindow -{ + +class MainWindow : public BWindow { public: - MainWindow(); - virtual ~MainWindow(); + MainWindow(); + virtual ~MainWindow(); - virtual void MessageReceived(BMessage* message); - virtual bool QuitRequested(); - virtual void SetVisible(bool visible); + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + + virtual void SetVisible(bool visible); + +private: + void _TogglePlay(StationListViewItem* stationItem); private: - RadioSettings* fSettings; - BMenuBar* fMainMenu; - StationListView* fStationList; - StationFinderWindow* fStationFinder; - StationPanel* fStationPanel; - BStringView* fStatusBar; - Expander* fExpander; - BObjectList activeStations; - bool allowParallelPlayback; - BMenuItem* menuParallelPlayback; - - void TogglePlay(StationListViewItem* stationItem); - - /** Actualizes the status in the status bar and in the tooltip of the system - * tray icon. */ - void AskForKradioripperImport(); - /** Displays the settings_general_dialog. */ - void DisplaySettings(); - /** Displays the tip of the day (independently from if the user - * has disabled them or not). */ - void DisplayTipOfDay(); - /** Sets the visibility of the streamdirectory and saves this value also in - * the config file. - * @param visible Choose \e true to show the streamdirectory and \e false to - * hide it. \note If #MainWindow is not visible, the streamdirectory will be - * hidden indepentendly of the parameter. */ - - /** Sets up the actions that are supported by this class. */ - void SetupActions(); - /** Sets up the dock widget with the stream directory. */ - void SetupStreamDirectory(); - void SaveSettings(); + RadioSettings* fSettings; + BMenuBar* fMainMenu; + StationListView* fStationList; + StationFinderWindow* fStationFinder; + StationPanel* fStationPanel; + BStringView* fStatusBar; + Expander* fExpander; + BObjectList fActiveStations; + bool fAllowParallelPlayback; + BMenuItem* fMenuParallelPlayback; }; -#endif + +#endif // _MAIN_WINDOW_H diff --git a/Makefile b/Makefile index 7c3bf43..8123bcd 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -## Haiku Generic Makefile v2.6 ## +## Haiku Generic Makefile v2.6 ## ## Fill in this file to specify the project being created, and the referenced ## Makefile-Engine will do all of the hard work for you. This handles any @@ -14,7 +14,7 @@ V_MINOR = 4 V_VARIETY = B_APPV_DEVELOPMENT V_BUILD = 1 -TARGET_DIR := ./dist +TARGET_DIR := dist PACKAGE = $(TARGET_DIR)/$(NAME)_$(VERSION)-$(ARCH).hpkg # The type of binary, must be one of: @@ -25,35 +25,34 @@ PACKAGE = $(TARGET_DIR)/$(NAME)_$(VERSION)-$(ARCH).hpkg TYPE = APP # If you plan to use localization, specify the application's MIME signature. -APP_MIME_SIG = application/x-vnd.Fishpond-Radio +APP_MIME_SIG = application/x-vnd.Fishpond-StreamRadio # The following lines tell Pe and Eddie where the SRCS, RDEFS, and RSRCS are # so that Pe and Eddie can fill them in for you. #%{ -# @src->@ +# @src->@ -# Specify the source files to use. Full paths or paths relative to the +# Specify the source files to use. Full paths or paths relative to the # Makefile can be included. All files, regardless of directory, will have # their object files created in the common object directory. Note that this # means this Makefile will not work correctly if two source files with the # same name (source.c or source.cpp) are included from different directories. # Also note that spaces in folder names do not work well with this Makefile. SRCS := About.cpp \ - Expander.cpp \ - HttpUtils.cpp \ - MainWindow.cpp \ - RadioApp.cpp \ - RadioSettings.cpp \ - Station.cpp \ - StationFinder.cpp \ - StationFinderListenLive.cpp \ - StationFinderRadioNetwork.cpp \ - StationListView.cpp \ - StationPanel.cpp \ - StreamIO.cpp \ - StreamPlayer.cpp \ - Utils.cpp - + Expander.cpp \ + HttpUtils.cpp \ + MainWindow.cpp \ + RadioApp.cpp \ + RadioSettings.cpp \ + Station.cpp \ + StationFinder.cpp \ + StationFinderListenLive.cpp \ + StationFinderRadioNetwork.cpp \ + StationListView.cpp \ + StationPanel.cpp \ + StreamIO.cpp \ + StreamPlayer.cpp \ + Utils.cpp # Specify the resource definition files to use. Full or relative paths can be # used. @@ -61,10 +60,10 @@ RDEFS = StreamRadio.rdef # Specify the resource files to use. Full or relative paths can be used. # Both RDEFS and RSRCS can be utilized in the same Makefile. -RSRCS = +RSRCS = # End Pe/Eddie support. -# @<-src@ +# @<-src@ #%} # Specify libraries to link against. @@ -86,20 +85,19 @@ LIBS = $(STDCPPLIBS) be translation bnetapi media localestub shared # to the Makefile. The paths included are not parsed recursively, so # include all of the paths where libraries must be found. Directories where # source files were specified are automatically included. -LIBPATHS = +LIBPATHS = # Additional paths to look for system headers. These use the form # "#include
". Directories that contain the files in SRCS are # NOT auto-included here. SYSTEM_INCLUDE_PATHS = /boot/system/develop/headers/private/media \ - /boot/system/develop/headers/os/codec \ - /boot/system/develop/headers/private/media/experimental \ - /boot/system/develop/headers/private/shared + /boot/system/develop/headers/private/media/experimental \ + /boot/system/develop/headers/private/shared # Additional paths paths to look for local headers. These use the form # #include "header". Directories that contain the files in SRCS are # automatically included. -LOCAL_INCLUDE_PATHS = +LOCAL_INCLUDE_PATHS = # Specify the level of optimization that you want. Specify either NONE (O0), # SOME (O1), FULL (O2), or leave blank (for the default optimization level). @@ -117,25 +115,25 @@ LOCALES = ca de en_GB en es ro # use. For example, setting DEFINES to "DEBUG=1" will cause the compiler # option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" would pass # "-DDEBUG" on the compiler's command line. -DEFINES = HAIKU_TARGET_PLATFORM_HAIKU +DEFINES = # Specify the warning level. Either NONE (suppress all warnings), # ALL (enable all warnings), or leave blank (enable default warnings). -WARNINGS = +WARNINGS = ALL # With image symbols, stack crawls in the debugger are meaningful. # If set to "TRUE", symbols will be created. -SYMBOLS := +SYMBOLS := # Includes debug information, which allows the binary to be debugged easily. # If set to "TRUE", debug info will be created. -DEBUGGER := +DEBUGGER := # Specify any additional compiler flags to be used. -COMPILER_FLAGS = +COMPILER_FLAGS = # Specify any additional linker flags to be used. -LINKER_FLAGS = +LINKER_FLAGS = # Specify the version of this binary. Example: # -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL" @@ -149,11 +147,11 @@ APP_VERSION := -app $(V_MAJOR) $(V_MIDDLE) $(V_MINOR) $(V_VARIETY) $(V_BUILD) -s # will instruct the "driverinstall" rule to place a symlink to your driver's # binary in ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will # appear at /dev/video/usb when loaded. The default is "misc". -DRIVER_PATH = +DRIVER_PATH = VERSION_RDEF = $(NAME)-version.rdef RDEFS += $(VERSION_RDEF) - + ## Include the Makefile-Engine DEVEL_DIRECTORY = \ $(shell findpaths -e B_FIND_PATH_DEVELOP_DIRECTORY etc/makefile-engine) @@ -168,7 +166,7 @@ ARCH_PACKAGE_INFO = $(OBJ_DIR)/PackageInfo_$(ARCH) $(ARCH_PACKAGE_INFO): Makefile cat ./PackageInfo | sed 's/$$VERSION/$(VERSION)/' | sed 's/$$ARCH/$(ARCH)/' > $(ARCH_PACKAGE_INFO) - + $(PACKAGE): $(TARGET_DIR)/$(NAME) $(ARCH_PACKAGE_INFO) mkdir -p $(PACKAGE_DIR)/apps mkdir -p $(PACKAGE_DIR)/data/deskbar/menu/Applications @@ -183,7 +181,7 @@ clean: rdefclean distclean rdefclean: -rm $(VERSION_RDEF) - + distclean: -rm $(TARGET_DIR)/$(NAME) diff --git a/PackageInfo b/PackageInfo index ff49442..4c67101 100644 --- a/PackageInfo +++ b/PackageInfo @@ -2,8 +2,8 @@ name StreamRadio version $VERSION summary "Find and play internet radio stations" description "Start the application. Run a search for a radio station name. If you found one you like, add it to the main list. There you can play it, visit its homepage or change the station information. -Searches use Radio Network, a service that offers a network API to its database (http://www.radio-browser.info). -Stations including all their attributes are saved as Shoutcast playlist files in ~/settings/Stations so they are available for other apps. +Searches use Radio Network, a service that offers a network API to its database (https://www.radio-browser.info/gui). +Stations including all their attributes are saved as Shoutcast playlist files in ~/config/settings/Stations so they are available for other apps. Please report back any bugs at /~https://github.com/HaikuArchives/StreamRadio/issues. @@ -37,5 +37,4 @@ provides { } requires { haiku - lib:libxml2 } diff --git a/README.md b/README.md index dacab2a..b5092bb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# Haiku-Radio +# StreamRadio ![Screenshot](screenshot.png) -Haiku native application to search for an listen to internet radio stations. +An application native to Haiku to search for and listen to internet radio stations. # [StreamRadio user guide](docs/userguide.md) diff --git a/RadioApp.cpp b/RadioApp.cpp index 2d8f6c0..24c1d0f 100644 --- a/RadioApp.cpp +++ b/RadioApp.cpp @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2017 Kai Niessen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + #include #include "About.h" @@ -6,13 +24,14 @@ #include "StationFinderListenLive.h" #include "StationFinderRadioNetwork.h" + #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "RadioApp" RadioApp::RadioApp() : - BApplication(appSignature), + BApplication(kAppSignature), fArgvMessage(NULL) { } @@ -28,7 +47,7 @@ RadioApp::ReadyToRun() { mainWindow = new MainWindow(); mainWindow->Show(); - if (fArgvMessage) + if (fArgvMessage != NULL) mainWindow->PostMessage(fArgvMessage); } @@ -44,6 +63,7 @@ void RadioApp::ArgvReceived(int32 argc, char** argv) { fArgvMessage = new BMessage(B_REFS_RECEIVED); + int32 count = 0; for (int32 i = 1; i < argc; i++) { char* arg = argv[i]; @@ -54,6 +74,7 @@ RadioApp::ArgvReceived(int32 argc, char** argv) "otherwise it is added.\n")); continue; } + BEntry entry(arg); if (entry.Exists()) { entry_ref entryRef; @@ -62,8 +83,9 @@ RadioApp::ArgvReceived(int32 argc, char** argv) count++; } } - if (count) { - if (mainWindow) + + if (count != 0) { + if (mainWindow != NULL) mainWindow->PostMessage(fArgvMessage); } else { delete fArgvMessage; @@ -78,6 +100,8 @@ RadioApp::AboutRequested() About* about = new About(); about->Show(); } + + /** * Application entry point */ @@ -85,9 +109,14 @@ int main(int argc, char* argv[]) { StationFinderRadioNetwork::RegisterSelf(); - StationFinderListenLive::RegisterSelf(); + + // FIXME: "listenlive.eu" no longer seems to exist, though it looks like + // "radiomap.eu" might be its successor? This plugin crashes after searching + // anyway... + // StationFinderListenLive::RegisterSelf(); new RadioApp(); be_app->Run(); + delete be_app; } diff --git a/RadioApp.h b/RadioApp.h index efb90d2..0738e29 100644 --- a/RadioApp.h +++ b/RadioApp.h @@ -1,36 +1,50 @@ /* - * File: RadioApp.h - * Author: Kai Niessen + * Copyright (C) 2017 Kai Niessen * - * Created on 26. Februar 2013, 02:40 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ +#ifndef _RADIO_APP_H +#define _RADIO_APP_H -#ifndef MAIN_H -#define MAIN_H -#include "MainWindow.h" -#include "RadioSettings.h" #include #include -#define appSignature "application/x-vnd.Fishpond-Radio" +#include "MainWindow.h" +#include "RadioSettings.h" -/** - * Application class - */ -class RadioApp : BApplication -{ + +#define kAppSignature "application/x-vnd.Fishpond-StreamRadio" + + +class RadioApp : BApplication { public: - RadioApp(); - ~RadioApp(); - virtual void ReadyToRun(); - virtual void RefsReceived(BMessage* message); - virtual void ArgvReceived(int32 argc, char** argv); - virtual void AboutRequested(); - MainWindow* mainWindow; - RadioSettings Settings; + RadioApp(); + ~RadioApp(); + + virtual void ReadyToRun(); + virtual void RefsReceived(BMessage* message); + virtual void ArgvReceived(int32 argc, char** argv); + virtual void AboutRequested(); + + MainWindow* mainWindow; + + RadioSettings Settings; private: - BMessage* fArgvMessage; + BMessage* fArgvMessage; }; -#endif /* MAIN_H */ + + +#endif // _RADIO_APP_H diff --git a/RadioSettings.cpp b/RadioSettings.cpp index e6fb1c6..5968db0 100644 --- a/RadioSettings.cpp +++ b/RadioSettings.cpp @@ -1,9 +1,21 @@ /* - * File: RadioSettings.cpp - * Author: user + * Copyright (C) 2017 Kai Niessen * - * Created on 26. Februar 2013, 04:01 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + + #include #include #include @@ -12,7 +24,8 @@ #include "Debug.h" #include "RadioSettings.h" -const char* SettingsFileName = "StreamRadio.settings"; + +const char* kSettingsFileName = "StreamRadio.settings"; StationsList::StationsList() @@ -24,7 +37,7 @@ StationsList::StationsList() StationsList::~StationsList() { - for (int i = CountItems() - 1; i >= 0; i--) + for (int32 i = CountItems() - 1; i >= 0; i--) BObjectList::RemoveItem(ItemAt(i), true); } @@ -34,6 +47,7 @@ StationsList::AddItem(Station* station) { if (FindItem(station->Name())) return false; + return BObjectList::AddItem(station); } @@ -41,9 +55,9 @@ StationsList::AddItem(Station* station) bool StationsList::RemoveItem(BString* stationName) { - Station* s = FindItem(stationName); - if (s) - return BObjectList::RemoveItem(s, true); + Station* station = FindItem(stationName); + if (station != NULL) + return BObjectList::RemoveItem(station, true); else return false; } @@ -59,11 +73,12 @@ StationsList::RemoveItem(Station* station) Station* StationsList::FindItem(BString* stationName) { - for (int i = 0; i < CountItems(); i++) { + for (int32 i = 0; i < CountItems(); i++) { Station* item = ItemAt(i); if (stationName->Compare(item->Name()->String()) == 0) return item; } + return NULL; } @@ -77,9 +92,10 @@ StationsList::Load() while ((status = stationsDir->GetNextEntry(&stationEntry)) == B_OK) { Station* station = Station::LoadFromPlsFile(stationEntry.Name()); - if (station && FindItem(station->Name()) == NULL) + if (station != NULL && FindItem(station->Name()) == NULL) AddItem(station); } + return B_OK; } @@ -96,6 +112,7 @@ StationsList::Save() while ((status = stationsDir->GetNextEntry(&stationEntry)) == B_OK) stationEntry.Remove(); + EachListItemIgnoreResult(this, &Station::Save); } @@ -105,7 +122,7 @@ RadioSettings::RadioSettings() BMessage() { Stations = new StationsList(); - Load(); + _Load(); } @@ -124,6 +141,27 @@ RadioSettings::~RadioSettings() } +status_t +RadioSettings::Save() +{ + status_t status; + BPath configPath; + BDirectory configDir; + BFile configFile; + status = find_directory(B_USER_SETTINGS_DIRECTORY, &configPath); + if (status != B_OK) + return status; + + configDir.SetTo(configPath.Path()); + status = configDir.CreateFile(kSettingsFileName, &configFile); + Flatten(&configFile); + + Stations->Save(); + + return B_OK; +} + + bool RadioSettings::GetAllowParallelPlayback() { @@ -154,7 +192,7 @@ RadioSettings::SetStationFinderName(const char* name) status_t -RadioSettings::Load() +RadioSettings::_Load() { status_t status; BPath configPath; @@ -166,28 +204,9 @@ RadioSettings::Load() return status; configDir.SetTo(configPath.Path()); - status = configFile.SetTo(&configDir, SettingsFileName, B_READ_ONLY); + status = configFile.SetTo(&configDir, kSettingsFileName, B_READ_ONLY); status = Unflatten(&configFile); status = Stations->Load(); - return status; -} - - -status_t -RadioSettings::Save() -{ - status_t status; - BPath configPath; - BDirectory configDir; - BFile configFile; - status = find_directory(B_USER_SETTINGS_DIRECTORY, &configPath); - if (status != B_OK) - return status; - configDir.SetTo(configPath.Path()); - status = configDir.CreateFile(SettingsFileName, &configFile); - Flatten(&configFile); - - Stations->Save(); - return B_OK; + return status; } diff --git a/RadioSettings.h b/RadioSettings.h index 71bd541..216036e 100644 --- a/RadioSettings.h +++ b/RadioSettings.h @@ -1,45 +1,63 @@ /* - * File: RadioSettings.h - * Author: user + * Copyright (C) 2017 Kai Niessen * - * Created on 26. Februar 2013, 04:01 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ +#ifndef _RADIO_SETTINGS_H +#define _RADIO_SETTINGS_H -#ifndef RADIOSETTINGS_H -#define RADIOSETTINGS_H -#include "Station.h" #include #include #include -class StationsList : public BObjectList -{ +#include "Station.h" + + +class StationsList : public BObjectList { public: - StationsList(); - virtual ~StationsList(); - virtual bool AddItem(Station* station); - bool RemoveItem(Station* station); - bool RemoveItem(BString* StationName); - Station* FindItem(BString* Name); - status_t Load(); - void Save(); + StationsList(); + virtual ~StationsList(); + + virtual bool AddItem(Station* station); + bool RemoveItem(Station* station); + bool RemoveItem(BString* StationName); + Station* FindItem(BString* Name); + + status_t Load(); + void Save(); }; -class RadioSettings : private BMessage -{ +class RadioSettings : private BMessage { public: - RadioSettings(); - RadioSettings(const RadioSettings& orig); - virtual ~RadioSettings(); - status_t Save(); - const char* StationFinderName(); - void SetStationFinderName(const char* name); - StationsList* Stations; - bool GetAllowParallelPlayback(); - void SetAllowParallelPlayback(bool set); + RadioSettings(); + RadioSettings(const RadioSettings& orig); + virtual ~RadioSettings(); + + status_t Save(); + + const char* StationFinderName(); + void SetStationFinderName(const char* name); + + bool GetAllowParallelPlayback(); + void SetAllowParallelPlayback(bool set); + + StationsList* Stations; private: - status_t Load(); + status_t _Load(); }; -#endif /* RADIOSETTINGS_H */ + + +#endif // _RADIO_SETTINGS_H diff --git a/Station.cpp b/Station.cpp index 41a63dc..c136d4f 100644 --- a/Station.cpp +++ b/Station.cpp @@ -1,17 +1,36 @@ /* - * File: Station.cpp - * Author: Kai Niessen, Jacob Secunda + * Copyright (C) 2017 Kai Niessen + * Copyright (C) 2020 Jacob Secunda * - * Created on 26. Februar 2013, 04:08 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + + #include "Station.h" -#include "Debug.h" -#include "HttpUtils.h" + +#include +#include +#include +#include +#include + #include #include #include #include #include +#include #include #include #include @@ -26,18 +45,17 @@ #include #include #include -#include -#include -#include -#include -#include -#include + +#include "Debug.h" +#include "HttpUtils.h" + #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "Station" -const char* SubDirStations = "Stations"; -const char* MimePls = "audio/x-scpls"; + +const char* kSubDirStations = "Stations"; +const char* kMimePls = "audio/x-scpls"; Station::Station(BString name, BString uri) @@ -46,6 +64,8 @@ Station::Station(BString name, BString uri) fStreamUrl(uri), fStationUrl(B_EMPTY_STRING), fGenre(B_EMPTY_STRING), + fCountry(B_EMPTY_STRING), + fLanguage(B_EMPTY_STRING), fSource(B_EMPTY_STRING), fMime(B_EMPTY_STRING), fEncoding(0), @@ -58,7 +78,7 @@ Station::Station(BString name, BString uri) fChannels(0), fFlags(0) { - checkFlags(); + CheckFlags(); if (Flags(STATION_URI_VALID) && !Flags(STATION_HAS_FORMAT)) Probe(); } @@ -73,17 +93,17 @@ Station::Station(const Station& orig) fCountry(orig.fCountry), fLanguage(orig.fLanguage), fSource(B_EMPTY_STRING), + fEncoding(orig.fEncoding), fRating(orig.fRating), fBitRate(orig.fBitRate), fSampleRate(orig.fSampleRate), fUniqueIdentifier(orig.fUniqueIdentifier), - fChannels(orig.fChannels), - fEncoding(orig.fEncoding), - fMetaInterval(orig.fMetaInterval) + fMetaInterval(orig.fMetaInterval), + fChannels(orig.fChannels) { fMime.SetTo(orig.fMime.Type()); fLogo = (orig.fLogo) ? new BBitmap(orig.fLogo) : NULL; - unsaved = true; + fUnsaved = true; } @@ -106,9 +126,11 @@ Station::LoadFromPlsFile(BString Name) { BEntry stationEntry; stationEntry.SetTo(StationDirectory(), Name); + Station* station = Load(Name, &stationEntry); - if (station) - station->unsaved = false; + if (station != NULL) + station->fUnsaved = false; + return station; } @@ -119,22 +141,24 @@ Station::Save() status_t status; BFile stationFile; BDirectory* stationDir = StationDirectory(); - - if (!stationDir) - return B_ERROR; + if (stationDir == NULL) + return B_NO_MEMORY; status = stationDir->CreateFile(fName, &stationFile, false); if (status != B_OK) return status; + BString content; content << "[playlist]\nNumberOfEntries=1\nFile1=" << fStreamUrl << "\n"; stationFile.Write( content.LockBuffer(-1), content.CountBytes(0, content.CountChars())); content.UnlockBuffer(); + status = stationFile.Lock(); + status = stationFile.WriteAttrString("META:url", &fStreamUrl.UrlString()); status = stationFile.WriteAttr( - "BEOS:TYPE", B_MIME_TYPE, 0, MimePls, strlen(MimePls)); + "BEOS:TYPE", B_MIME_TYPE, 0, kMimePls, strlen(kMimePls)); status = stationFile.WriteAttr( "META:bitrate", B_INT32_TYPE, 0, &fBitRate, sizeof(fBitRate)); status = stationFile.WriteAttr( @@ -160,13 +184,14 @@ Station::Save() status = stationFile.WriteAttrString("META:uniqueidentifier", &fUniqueIdentifier); status = stationFile.Unlock(); + BNodeInfo stationInfo; stationInfo.SetTo(&stationFile); - if (fLogo) { + if (fLogo != NULL) { BMessage archive; fLogo->Archive(&archive); ssize_t archiveSize = archive.FlattenedSize(); - char* archiveBuffer = (char*) malloc(archiveSize); + char* archiveBuffer = (char*)malloc(archiveSize); archive.Flatten(archiveBuffer, archiveSize); stationFile.WriteAttr("logo", 'BBMP', 0LL, archiveBuffer, archiveSize); free(archiveBuffer); @@ -174,12 +199,14 @@ Station::Save() BBitmap* icon = new BBitmap( BRect(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1), B_RGB32, true); BView* canvas = new BView(icon->Bounds(), "canvas", B_FOLLOW_NONE, 0); + icon->AddChild(canvas); canvas->LockLooper(); canvas->DrawBitmap(fLogo, fLogo->Bounds(), icon->Bounds()); canvas->UnlockLooper(); icon->RemoveChild(canvas); stationInfo.SetIcon(icon, B_LARGE_ICON); + delete icon; icon = new BBitmap(BRect(0, 0, 15, 15), B_RGB32, true); @@ -191,11 +218,14 @@ Station::Save() canvas->UnlockLooper(); icon->RemoveChild(canvas); stationInfo.SetIcon(icon, B_MINI_ICON); + delete icon; delete canvas; } - stationInfo.SetType(MimePls); + + stationInfo.SetType(kMimePls); stationFile.Unset(); + return status; } @@ -208,16 +238,20 @@ Station::RetrieveStreamUrl() status_t status = B_ERROR; BString contentType("*/*"); - BMallocIO* plsData - = HttpUtils::GetAll(fSource, NULL, 100000, &contentType, 2000); - if (plsData) - status - = parseUrlReference((const char*) plsData->Buffer(), contentType); - delete plsData; + + BMallocIO* plsData = HttpUtils::GetAll(fSource, NULL, 100000, &contentType, + 2000); + if (plsData != NULL) { + status = ParseUrlReference((const char*) plsData->Buffer(), + contentType); + delete plsData; + } + if (status != B_OK && contentType.StartsWith("audio/")) { SetStreamUrl(fSource); return B_OK; } + return status; } @@ -254,57 +288,69 @@ Station::Probe() // of streams using HTTPS and load balancing between two or more different // IP's should be small, anyway. - if (fStreamUrl.Protocol() == "https") - buffer = HttpUtils::GetAll( - fStreamUrl, &headers, 2 * 1000 * 1000, &contentType, 4096); - else { + if (fStreamUrl.Protocol() == "https") { + buffer = HttpUtils::GetAll(fStreamUrl, &headers, 2 * 1000 * 1000, + &contentType, 4096); + } else { status_t resolveStatus = HttpUtils::CheckPort(fStreamUrl, resolvedUrl); if (resolveStatus != B_OK) return B_ERROR; + buffer = HttpUtils::GetAll( *resolvedUrl, &headers, 2 * 1000 * 1000, &contentType, 4096); delete resolvedUrl; } + #ifdef DEBUGGING - for (int i = 0; i < headers.CountHeaders(); i++) + for (int32 i = 0; i < headers.CountHeaders(); i++) TRACE("Header: %s\r\n", headers.HeaderAt(i).Header()); #endif + if (buffer == NULL) { fFlags &= !STATION_URI_VALID; return B_ERROR; } + if (headers.CountHeaders() == 0) { fFlags &= !STATION_URI_VALID; return B_TIMED_OUT; } - int index; + int32 index; if ((index = headers.HasHeader("content-type")) >= 0) fMime.SetTo(headers.HeaderValue("content-type")); + // If the station has no name, try to use the Icy-Name header if ((index = headers.HasHeader("Icy-Name")) >= 0 && fName.IsEmpty()) SetName(headers.HeaderValue("Icy-Name")); + if ((index = headers.HasHeader("Icy-Br")) >= 0) fBitRate = atoi(headers[index].Value()) * 1000; + if ((index = headers.HasHeader("Icy-Genre")) >= 0) fGenre = headers[index].Value(); + if ((index = headers.HasHeader("Icy-Url")) >= 0 && strlen(headers[index].Value()) > 0) fStationUrl.SetUrlString(headers[index].Value()); + if ((index = headers.HasHeader("Ice-Audio-Info")) >= 0) { BString audioInfo(headers[index].Value()); BStringList audioInfoList; if (audioInfo.Split(";", false, audioInfoList)) { for (int32 i = 0; i < audioInfoList.CountStrings(); i++) { BString audioInfoItem = audioInfoList.StringAt(i); + if (audioInfoItem.StartsWith("ice-samplerate=")) { audioInfoItem.Remove(0, 15); fSampleRate = atoi(audioInfoItem.String()); } + if (audioInfoItem.StartsWith("ice-bitrate=")) { audioInfoItem.Remove(0, 12); fBitRate = atoi(audioInfoItem.String()) * 1000; } + if (audioInfoItem.StartsWith("ice-channels=")) { audioInfoItem.Remove(0, 13); fChannels = atoi(audioInfoItem.String()); @@ -312,13 +358,15 @@ Station::Probe() } } } + if ((index = headers.HasHeader("Icy-metaint")) >= 0) fMetaInterval = atoi(headers[index].Value()); - checkFlags(); - unsaved = true; + CheckFlags(); + fUnsaved = true; ProbeBuffer(buffer); delete buffer; + return B_OK; } @@ -328,7 +376,9 @@ Station::ProbeBuffer(BPositionIO* buffer) { status_t status = B_OK; BMediaFile mediaFile(buffer); - if ((status = mediaFile.InitCheck()) != B_OK) + + status = mediaFile.InitCheck(); + if (status != B_OK) return status; if (mediaFile.CountTracks() < 1) @@ -336,8 +386,8 @@ Station::ProbeBuffer(BPositionIO* buffer) BMediaTrack* mediaTrack = mediaFile.TrackAt(0); - media_format encodedFormat, decodedFormat; - media_codec_info codecInfo; + media_format encodedFormat; + media_format decodedFormat; status = mediaTrack->EncodedFormat(&encodedFormat); if (status != B_OK) @@ -350,74 +400,97 @@ Station::ProbeBuffer(BPositionIO* buffer) fFrameSize = encodedFormat.u.encoded_audio.frame_size; status = mediaTrack->DecodedFormat(&decodedFormat); - checkFlags(); + CheckFlags(); + return status; } status_t -Station::parseUrlReference(const char* body, const char* mime) +Station::ParseUrlReference(const char* body, const char* mime) { - const char* patterns[4] = {"^file[0-9]+=([^\r\n]*)[\r\n$]+", // ShoutcastUrl "^(http://[^\r\n]*)[\r\n]+$", // Mpeg Url; "^([^#]+[^\r\n]*)[\r\n]+$", // Mpeg Url; "^title[0-9]+=([^\r\n]*)[\r\n$]+"}; // Shoutcast alternativ; - for (int i = 0; i < 3; i++) { - char* match = regFind(body, patterns[i]); - if (match) { + for (int32 i = 0; i < 3; i++) { + char* match = RegFind(body, patterns[i]); + if (match != NULL) { fStreamUrl.SetUrlString(match); free(match); - match = regFind(body, patterns[3]); - if (match) { + + match = RegFind(body, patterns[3]); + if (match != NULL) { SetName(match); free(match); } + return B_OK; } } + return B_ERROR; } Station* -Station::Load(BString Name, BEntry* entry) +Station::Load(BString name, BEntry* entry) { off_t size; status_t status; - Station* station = new Station(Name); + + Station* station = new Station(name); + if (station == NULL) + return station; + BFile file; file.SetTo(entry, B_READ_ONLY); BNodeInfo stationInfo; stationInfo.SetTo(&file); BString readString; + status = file.ReadAttrString("META:url", &readString); + station->fStreamUrl.SetUrlString(readString); + status = file.ReadAttrString("META:genre", &station->fGenre); + status = file.ReadAttrString("META:country", &station->fCountry); + status = file.ReadAttrString("META:language", &station->fLanguage); + status = file.ReadAttr("META:bitrate", B_INT32_TYPE, 0, &station->fBitRate, sizeof(station->fBitRate)); + status = file.ReadAttr("META:rating", B_INT32_TYPE, 0, &station->fRating, sizeof(station->fRating)); + status = file.ReadAttr("META:interval", B_INT32_TYPE, 0, &station->fMetaInterval, sizeof(station->fMetaInterval)); + status = file.ReadAttr("META:samplerate", B_INT32_TYPE, 0, &station->fSampleRate, sizeof(station->fSampleRate)); + status = file.ReadAttr("META:channels", B_INT32_TYPE, 0, &station->fChannels, sizeof(station->fChannels)); + status = file.ReadAttr("META:encoding", B_INT32_TYPE, 0, &station->fEncoding, sizeof(station->fEncoding)); + status = file.ReadAttr("META:framesize", B_INT32_TYPE, 0, &station->fFrameSize, sizeof(station->fFrameSize)); + status = file.ReadAttrString("META:mime", &readString); station->fMime.SetTo(readString); + status = file.ReadAttrString("META:source", &readString); station->fSource.SetUrlString(readString); + status = file.ReadAttrString("META:stationurl", &readString); station->fStationUrl.SetUrlString(readString); + status = file.ReadAttrString("META:uniqueidentifier", &readString); station->fUniqueIdentifier.SetTo(readString); @@ -432,9 +505,11 @@ Station::Load(BString Name, BEntry* entry) station->fLogo = (BBitmap*) BBitmap::Instantiate(&archive); } else station->fLogo = new BBitmap(BRect(0, 0, 32, 32), B_RGB32); + status = stationInfo.GetIcon(station->fLogo, B_LARGE_ICON); if (status != B_OK) delete station->fLogo; + station->fLogo = new BBitmap(BRect(0, 0, 16, 16), B_RGB32); status = stationInfo.GetIcon(station->fLogo, B_MINI_ICON); if (status != B_OK) { @@ -452,70 +527,86 @@ Station::Load(BString Name, BEntry* entry) status = file.GetSize(&size); if (size > 10000) return NULL; - char* buffer = (char*) malloc(size); + + char* buffer = (char*)malloc(size); file.Read(buffer, size); + char mime[64]; stationInfo.GetType(mime); - station->parseUrlReference(buffer, mime); + station->ParseUrlReference(buffer, mime); + free(buffer); } - if (Name == B_EMPTY_STRING) + + if (name == B_EMPTY_STRING) station->fName.SetTo(entry->Name()); - station->checkFlags(); + station->CheckFlags(); if (station->InitCheck() != B_OK) { delete station; return NULL; - } else - return station; + } + + return station; } char* -Station::regFind(const char* Text, const char* Pattern) +Station::RegFind(const char* text, const char* pattern) { regex_t patternBuffer; regmatch_t matchBuffer[20]; int result; char* match = NULL; + memset(&patternBuffer, 0, sizeof(patternBuffer)); memset(matchBuffer, 0, sizeof(matchBuffer)); + result = regcomp( - &patternBuffer, Pattern, REG_EXTENDED | REG_NEWLINE | REG_ICASE); - result = regexec(&patternBuffer, Text, 20, matchBuffer, 0); - if (result == 0 && matchBuffer[1].rm_eo > -1) - match = strndup(Text + matchBuffer[1].rm_so, + &patternBuffer, pattern, REG_EXTENDED | REG_NEWLINE | REG_ICASE); + result = regexec(&patternBuffer, text, 20, matchBuffer, 0); + if (result == 0 && matchBuffer[1].rm_eo > -1) { + match = strndup(text + matchBuffer[1].rm_so, matchBuffer[1].rm_eo - matchBuffer[1].rm_so); + } + return match; } Station* -Station::LoadIndirectUrl(BString& sUrl) +Station::LoadIndirectUrl(BString& shoutCastUrl) { status_t status; const char* patternTitle = "]*>(.*?)]*>"; const char* patternIcon = "Write("", 1); - const char* body = (char*) dataIO->Buffer(); + const char* body = (char*)dataIO->Buffer(); + int32 pos = contentType.FindFirst(';'); if (pos >= 0) contentType.Truncate(pos); - status = station->parseUrlReference(body, contentType.String()); + + status = station->ParseUrlReference(body, contentType.String()); if (status != B_OK && contentType.StartsWith(("audio/"))) station->SetStreamUrl(url); - station->fSource.SetUrlString(sUrl); + + station->fSource.SetUrlString(shoutCastUrl); + delete dataIO; if (!station->fStreamUrl.IsValid()) { @@ -527,7 +618,6 @@ Station::LoadIndirectUrl(BString& sUrl) * Check for name and logo on same server by calling main page */ - BUrl finalUrl = station->fStationUrl; if ((!finalUrl.HasPort() || finalUrl.Port() == 80) && (!finalUrl.HasPath() || finalUrl.Path().IsEmpty() @@ -536,32 +626,35 @@ Station::LoadIndirectUrl(BString& sUrl) station->SetName("New Station"); } else finalUrl.SetFragment(NULL); + finalUrl.SetRequest(NULL); finalUrl.SetPath(NULL); finalUrl.SetPort(80); - sUrl.SetTo(finalUrl.UrlString()); - sUrl.RemoveCharsSet("#?"); - finalUrl.SetUrlString(sUrl); + + shoutCastUrl.SetTo(finalUrl.UrlString()); + shoutCastUrl.RemoveCharsSet("#?"); + finalUrl.SetUrlString(shoutCastUrl); dataIO = HttpUtils::GetAll(finalUrl); - if (dataIO) { + if (dataIO != NULL) { dataIO->Write(&"", 1); body = (char*) dataIO->Buffer(); - char* title = regFind(body, patternTitle); - if (title) + char* title = RegFind(body, patternTitle); + if (title != NULL) station->fName.SetTo(title); - char* icon = regFind(body, patternIcon); - if (icon) + char* icon = RegFind(body, patternIcon); + if (icon != NULL) finalUrl.SetPath(BString(icon)); contentType = "image/*"; BMallocIO* iconIO = HttpUtils::GetAll(finalUrl, NULL, 10000, &contentType, 2000); - if (iconIO) { + if (iconIO != NULL) { iconIO->Seek(0, SEEK_SET); station->fLogo = BTranslationUtils::GetBitmap(iconIO); + delete iconIO; } @@ -585,74 +678,85 @@ Station::SetName(BString name) } fName.SetTo(name); - cleanName(); + CleanName(); if (fName.IsEmpty()) fFlags &= !STATION_HAS_NAME; else fFlags |= STATION_HAS_NAME; - if (entry) { + if (entry != NULL) { entry->Rename(fName); delete entry; Save(); } else - unsaved = true; + fUnsaved = true; } void -Station::cleanName() +Station::CleanName() { - if (fName.Compare("(#", 2) == 0) - if (0 <= fName.FindFirst(')') < fName.Length()) - fName.Remove(0, fName.FindFirst(')') + 1).Trim(); + if (fName.Compare("(#", 2) == 0 + && fName.FindFirst(')') >= 0 + && fName.FindFirst(')') < fName.Length()) + fName.Remove(0, fName.FindFirst(')') + 1).Trim(); fName.RemoveCharsSet("\\/#?"); } void -Station::checkFlags() +Station::CheckFlags() { fFlags = 0; + if (!fName.IsEmpty()) fFlags |= STATION_HAS_NAME; + if (fStreamUrl.HasHost()) fFlags |= STATION_HAS_URI; + if (fStreamUrl.IsValid()) fFlags |= STATION_URI_VALID; + if (fBitRate != 0) fFlags |= STATION_HAS_BITRATE; + if (fMime.IsValid() && fEncoding != 0) fFlags |= STATION_HAS_ENCODING; + if (fChannels != 0 && fSampleRate != 0) fFlags |= STATION_HAS_FORMAT; + if (fMetaInterval != 0) fFlags |= STATION_HAS_META; + if (!fUniqueIdentifier.IsEmpty()) fFlags |= STATION_HAS_IDENTIFIER; } -BDirectory* Station::fStationsDirectory = NULL; +BDirectory* Station::sStationsDirectory = NULL; BDirectory* Station::StationDirectory() { - if (fStationsDirectory) - return fStationsDirectory; + if (sStationsDirectory != NULL) + return sStationsDirectory; - status_t status; BPath configPath; BDirectory configDir; - status = find_directory(B_USER_SETTINGS_DIRECTORY, &configPath); + if (find_directory(B_USER_SETTINGS_DIRECTORY, &configPath) != B_OK) + configPath.SetTo("/boot/home/config/settings"); + configDir.SetTo(configPath.Path()); - if (configDir.Contains(SubDirStations, B_DIRECTORY_NODE)) - fStationsDirectory = new BDirectory(&configDir, SubDirStations); + if (configDir.Contains(kSubDirStations, B_DIRECTORY_NODE)) + sStationsDirectory = new BDirectory(&configDir, kSubDirStations); else { - fStationsDirectory = new BDirectory(); - configDir.CreateDirectory(SubDirStations, fStationsDirectory); + sStationsDirectory = new BDirectory(); + configDir.CreateDirectory(kSubDirStations, sStationsDirectory); + BAlert* alert = new BAlert(B_TRANSLATE("Stations directory created"), B_TRANSLATE( "A directory for saving stations has been created in your " @@ -662,5 +766,5 @@ Station::StationDirectory() alert->Go(); } - return fStationsDirectory; + return sStationsDirectory; } diff --git a/Station.h b/Station.h index 5d0e0a0..eb46ca4 100644 --- a/Station.h +++ b/Station.h @@ -1,20 +1,33 @@ /* - * File: Station.h - * Author: Kai Niessen, Jacob Secunda + * Copyright (C) 2017 Kai Niessen + * Copyright (C) 2020 Jacob Secunda * - * Created on 26. Februar 2013, 04:08 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ +#ifndef _STATION_H +#define _STATION_H + -#ifndef STATION_H -#define STATION_H -#include "HttpUtils.h" #include #include #include #include #include -#include -#include +#include +#include + +#include "HttpUtils.h" // Station flags @@ -30,116 +43,138 @@ class StreamPlayer; -class Station -{ + +class Station { public: - Station(BString Name, BString Uri = B_EMPTY_STRING); - Station(const Station& orig); - virtual ~Station(); - status_t InitCheck(); - status_t Save(); - status_t RetrieveStreamUrl(); - status_t Probe(); - status_t ProbeBuffer(BPositionIO* buffer); - - static class Station* LoadFromPlsFile(BString Name); - static class Station* Load(BString Name, BEntry* entry); - static class Station* LoadIndirectUrl(BString& ShoutCastUrl); - static BDirectory* StationDirectory(); - - inline BString* Name() { return &fName; } - void SetName(BString name); - inline BUrl StreamUrl() { return fStreamUrl; } - inline void SetStreamUrl(BUrl uri) + Station(BString name, + BString uri = B_EMPTY_STRING); + Station(const Station& orig); + virtual ~Station(); + + status_t InitCheck(); + status_t Save(); + status_t RetrieveStreamUrl(); + status_t Probe(); + status_t ProbeBuffer(BPositionIO* buffer); + + static class Station* LoadFromPlsFile(BString name); + static class Station* Load(BString name, BEntry* entry); + static class Station* LoadIndirectUrl(BString& shoutCastUrl); + + static BDirectory* StationDirectory(); + + inline BString* Name() { return &fName; } + void SetName(BString name); + + inline BUrl StreamUrl() { return fStreamUrl; } + inline void SetStreamUrl(BUrl uri) { fStreamUrl = uri; - checkFlags(); - unsaved = true; + CheckFlags(); + fUnsaved = true; } - inline BUrl StationUrl() { return fStationUrl; } - inline void SetStation(BUrl sUrl) + + inline BUrl StationUrl() { return fStationUrl; } + inline void SetStation(BUrl url) { - fStationUrl = sUrl; - checkFlags(); - unsaved = true; + fStationUrl = url; + CheckFlags(); + fUnsaved = true; } - inline BUrl Source() { return fSource; } - inline void SetSource(BUrl source) + + inline BUrl Source() { return fSource; } + inline void SetSource(BUrl source) { fSource = source; - checkFlags(); - unsaved = true; + CheckFlags(); + fUnsaved = true; } - inline BBitmap* Logo() { return fLogo; } - void SetLogo(BBitmap* logo) + + inline BBitmap* Logo() { return fLogo; } + inline void SetLogo(BBitmap* logo) { - if (fLogo) - delete fLogo; + delete fLogo; + fLogo = logo; - unsaved = true; + fUnsaved = true; } - inline BString Genre() { return fGenre; } - inline void SetGenre(BString genre) + + inline BString Genre() { return fGenre; } + inline void SetGenre(BString genre) { fGenre.SetTo(genre); - unsaved = true; + fUnsaved = true; } - inline BString Country() { return fCountry; } - inline void SetCountry(BString country) + + inline BString Country() { return fCountry; } + inline void SetCountry(BString country) { fCountry.SetTo(country); - unsaved = true; + fUnsaved = true; } - inline BString Language() { return fLanguage; } - inline void SetLanguage(BString language) + + inline BString Language() { return fLanguage; } + inline void SetLanguage(BString language) { fLanguage.SetTo(language); - unsaved = true; + fUnsaved = true; } - inline int32 SampleRate() { return fSampleRate; } - inline int32 BitRate() { return fBitRate; } - void SetBitRate(int32 bitrate) + + inline int32 SampleRate() { return fSampleRate; } + inline int32 BitRate() { return fBitRate; } + inline void SetBitRate(int32 bitrate) { fBitRate = bitrate; - unsaved = true; + fUnsaved = true; } - inline BString UniqueIdentifier() { return fUniqueIdentifier; } - void SetUniqueIdentifier(BString uniqueIdentifier) + + inline BString UniqueIdentifier() { return fUniqueIdentifier; } + inline void SetUniqueIdentifier(BString uniqueIdentifier) { fUniqueIdentifier.SetTo(uniqueIdentifier); - unsaved = true; + fUnsaved = true; } - inline int32 Channels() { return fChannels; } - inline int32 Encoding() { return fEncoding; } - inline size_t FrameSize() { return fFrameSize; } - inline BMimeType* Mime() { return &fMime; } - inline int32 Flags() { return fFlags; } - inline bool Flags(int32 flags) { return (fFlags & flags) == flags; } + + inline int32 Channels() { return fChannels; } + inline int32 Encoding() { return fEncoding; } + inline size_t FrameSize() { return fFrameSize; } + inline BMimeType* Mime() { return &fMime; } + + inline int32 Flags() { return fFlags; } + inline bool Flags(uint32 flags) + { return (fFlags & flags) == flags; } protected: - void checkFlags(); - static char* regFind(const char* Text, const char* Pattern); - status_t parseUrlReference(const char* body, const char* mime); - void cleanName(); - bool unsaved; - BString fName; - BUrl fStreamUrl; - BUrl fStationUrl; - BBitmap* fLogo; - uint32 fRating; - BString fGenre; - BString fCountry; - BString fLanguage; - BUrl fSource; - uint32 fBitRate; - uint32 fSampleRate; - BString fUniqueIdentifier; - uint32 fChannels; - size_t fFrameSize; - BMimeType fMime; - uint32 fEncoding; - uint32 fMetaInterval; - uint32 fFlags; - static BDirectory* fStationsDirectory; + void CheckFlags(); + static char* RegFind(const char* text, const char* pattern); + status_t ParseUrlReference(const char* body, + const char* mime); + void CleanName(); + + BString fName; + BUrl fStreamUrl; + BUrl fStationUrl; + BString fGenre; + BString fCountry; + BString fLanguage; + BUrl fSource; + BMimeType fMime; + uint32 fEncoding; + BBitmap* fLogo; + uint32 fRating; + uint32 fBitRate; + uint32 fSampleRate; + BString fUniqueIdentifier; + uint32 fMetaInterval; + uint32 fChannels; + uint32 fFlags; + size_t fFrameSize; + + static BDirectory* sStationsDirectory; + +private: + bool fUnsaved; }; -#endif /* STATION_H */ + + +#endif // _STATION_H diff --git a/StationFinder.cpp b/StationFinder.cpp index 585e036..31fd819 100644 --- a/StationFinder.cpp +++ b/StationFinder.cpp @@ -11,39 +11,45 @@ #include #include + #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "StationFinder" +std::vector > + StationFinderServices::sServices; + + StationFinderServices::~StationFinderServices() { - fServices.clear(); + sServices.clear(); } void StationFinderServices::Register(char* serviceName, InstantiateFunc instantiate) { - pair service(serviceName, instantiate); - fServices.push_back(service); + std::pair service(serviceName, instantiate); + sServices.push_back(service); } -int +int32 StationFinderServices::CountItems() { - return fServices.size(); + return sServices.size(); } StationFinderService* StationFinderServices::Instantiate(char* name) { - for (int i = 0; i < fServices.size(); i++) { - pair service = fServices[i]; + for (uint32 i = 0; i < sServices.size(); i++) { + std::pair service = sServices[i]; if (!strcmp(service.first, name)) return service.second(); } + return NULL; } @@ -51,11 +57,9 @@ StationFinderServices::Instantiate(char* name) char* StationFinderServices::Name(int i) { - return fServices[i].first; + return sServices[i].first; } -vector > StationFinderServices::fServices; - FindByCapability::FindByCapability(char* name) : @@ -65,12 +69,12 @@ FindByCapability::FindByCapability(char* name) } -FindByCapability::FindByCapability(char* name, char* KeyWords, char* delimiter) +FindByCapability::FindByCapability(char* name, char* keyWords, char* delimiter) : fName(name), fKeywords() { - SetKeyWords(KeyWords, delimiter); + SetKeyWords(keyWords, delimiter); } @@ -102,9 +106,9 @@ FindByCapability::Name() void -FindByCapability::SetKeyWords(char* KeyWords, char* delimiter) +FindByCapability::SetKeyWords(char* keyWords, char* delimiter) { - BString tmp(KeyWords); + BString tmp(keyWords); tmp.Split(delimiter, true, fKeywords); } @@ -121,8 +125,7 @@ StationFinderService::StationFinderService() StationFinderService::~StationFinderService() { - if (serviceLogo) - delete serviceLogo; + delete serviceLogo; findByCapabilities.MakeEmpty(true); } @@ -135,19 +138,20 @@ StationFinderService::Register(char* name, InstantiateFunc instantiate) BBitmap* -StationFinderService::logo(BUrl url) +StationFinderService::RetrieveLogo(BUrl url) { BBitmap* bm = NULL; BString contentType("image/*"); - BMallocIO* bmData = HttpUtils::GetAll(url, NULL, 3000, &contentType); - - if (bmData) { + BMallocIO* bmData = HttpUtils::GetAll(url, NULL, 3000, &contentType); + if (bmData != NULL) { bm = BTranslationUtils::GetBitmap(bmData); - if (bm == NULL || bm->InitCheck() != B_OK) - bm == NULL; + if (bm != NULL && bm->InitCheck() != B_OK) + bm = NULL; + delete bmData; } + return bm; } @@ -157,6 +161,7 @@ StationFinderService::RegisterSearchCapability(char* name) { FindByCapability* newCapability = new FindByCapability(name); findByCapabilities.AddItem(newCapability); + return newCapability; } @@ -168,6 +173,7 @@ StationFinderService::RegisterSearchCapability( FindByCapability* newCapability = new FindByCapability(name, keywords, delimiter); findByCapabilities.AddItem(newCapability); + return newCapability; } @@ -176,95 +182,105 @@ StationFinderWindow::StationFinderWindow(BWindow* parent) : BWindow(BRect(0, 0, 300, 150), B_TRANSLATE("Find stations"), B_TITLED_WINDOW, B_CLOSE_ON_ESCAPE), - currentService(NULL) + fCurrentService(NULL) { - messenger = new BMessenger(parent); + fMessenger = new BMessenger(parent); CenterIn(parent->Frame()); - txSearch = new BTextControl(NULL, NULL, new BMessage(MSG_TXSEARCH)); - kwSearch = new BOptionPopUp("kwSearch", NULL, new BMessage(MSG_KWSEARCH)); - bnSearch = new BButton("bnSearch", NULL, new BMessage(MSG_BNSEARCH)); - bnSearch->SetIcon(Utils::ResourceBitmap(RES_BN_SEARCH)); + fTxSearch = new BTextControl(NULL, NULL, new BMessage(MSG_TXSEARCH)); + + fKwSearch = new BOptionPopUp("fKwSearch", NULL, new BMessage(MSG_KWSEARCH)); + fBnSearch = new BButton("fBnSearch", NULL, new BMessage(MSG_BNSEARCH)); + fBnSearch->SetIcon(Utils::ResourceBitmap(RES_BN_SEARCH)); - ddServices = new BOptionPopUp( - "ddServices", NULL, new BMessage(MSG_SELECT_SERVICE)); + fDdServices = new BOptionPopUp( + "fDdServices", NULL, new BMessage(MSG_SELECT_SERVICE)); int currentServiceIndex = 0; const char* settingsServiceName - = ((RadioApp*) be_app)->Settings.StationFinderName(); - for (int i = 0; i < StationFinderServices::CountItems(); i++) { + = ((RadioApp*)be_app)->Settings.StationFinderName(); + for (int32 i = 0; i < StationFinderServices::CountItems(); i++) { const char* serviceName = StationFinderServices::Name(i); if (settingsServiceName && !strcmp(serviceName, settingsServiceName)) currentServiceIndex = i; - ddServices->AddOption(serviceName, i); + fDdServices->AddOption(serviceName, i); } - bnVisit = new BButton("bnVisit", "", new BMessage(MSG_VISIT_SERVICE)); - bnVisit->SetIcon(Utils::ResourceBitmap(RES_BN_WEB)); - ddSearchBy - = new BOptionPopUp("ddSearchBy", NULL, new BMessage(MSG_SEARCH_BY)); - resultView = new StationListView(); - resultView->SetExplicitAlignment( + fBnVisit = new BButton("fBnVisit", "", new BMessage(MSG_VISIT_SERVICE)); + fBnVisit->SetIcon(Utils::ResourceBitmap(RES_BN_WEB)); + + fDdSearchBy + = new BOptionPopUp("fDdSearchBy", NULL, new BMessage(MSG_SEARCH_BY)); + + fResultView = new StationListView(); + fResultView->SetExplicitAlignment( BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT)); - bnAdd = new BButton( - "bnAdd", B_TRANSLATE("Add"), new BMessage(MSG_ADD_STATION)); + + fBnAdd = new BButton( + "fBnAdd", B_TRANSLATE("Add"), new BMessage(MSG_ADD_STATION)); + BLayoutBuilder::Group<>(this, B_VERTICAL, 0) .AddGrid(3, 3, 0.0) - .SetInsets(B_USE_WINDOW_INSETS) - .Add(new BStringView("lbServices", B_TRANSLATE("Service")), 0, 0) - .Add(ddServices, 1, 0) - .Add(bnVisit, 2, 0) - - .Add(new BStringView("lbSearchBy", B_TRANSLATE("Search by")), 0, 1) - .Add(ddSearchBy, 1, 1) - - .Add(new BStringView("lbSearchFor", B_TRANSLATE("Search for")), 0, 2) - .Add(txSearch, 1, 2) - .Add(kwSearch, 1, 2) - .Add(bnSearch, 2, 2) - .GetLayout(&searchGrid) + .SetInsets(B_USE_WINDOW_INSETS) + .Add(new BStringView("lbServices", B_TRANSLATE("Service")), 0, 0) + .Add(fDdServices, 1, 0) + .Add(fBnVisit, 2, 0) + + .Add(new BStringView("lbSearchBy", B_TRANSLATE("Search by")), 0, 1) + .Add(fDdSearchBy, 1, 1) + + .Add(new BStringView( + "lbSearchFor", B_TRANSLATE("Search for")), 0, 2) + .Add(fTxSearch, 1, 2) + .Add(fKwSearch, 1, 2) + .Add(fBnSearch, 2, 2) + .GetLayout(&fSearchGrid) .End() - .Add(new BScrollView( - "srollResult", resultView, 0, B_FOLLOW_ALL_SIDES, false, true), - 0.9) + .Add(new BScrollView("srollResult", fResultView, 0, false, true), 0.9) + .AddGrid(3, 1, 0.0) - .SetInsets(B_USE_WINDOW_INSETS) - .Add(bnAdd, 2, 0) - .SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_VERTICAL_UNSET)) + .SetInsets(B_USE_WINDOW_INSETS) + .Add(fBnAdd, 2, 0) + .SetExplicitAlignment( + BAlignment(B_ALIGN_RIGHT, B_ALIGN_VERTICAL_UNSET)) .End() - .End(); + .End(); + + fBnAdd->SetEnabled(false); + + fResultView->SetInvocationMessage(new BMessage(MSG_ADD_STATION)); + fResultView->SetSelectionMessage(new BMessage(MSG_SELECT_STATION)); - bnAdd->SetEnabled(false); - resultView->SetInvocationMessage(new BMessage(MSG_ADD_STATION)); - resultView->SetSelectionMessage(new BMessage(MSG_SELECT_STATION)); Layout(true); ResizeToPreferred(); ResizeBy(100, 0); - txSearch->MakeFocus(true); - bnSearch->MakeDefault(true); - txSearch->StartWatchingAll(this); + fTxSearch->MakeFocus(true); + fBnSearch->MakeDefault(true); + fTxSearch->StartWatchingAll(this); + SelectService(currentServiceIndex); - ddServices->SetValue(currentServiceIndex); + fDdServices->SetValue(currentServiceIndex); } StationFinderWindow::~StationFinderWindow() { - if (currentService) - delete currentService; - resultView->MakeEmpty(); - txSearch->StopWatchingAll(this); - delete messenger; + delete fCurrentService; + + fResultView->MakeEmpty(); + fTxSearch->StopWatchingAll(this); + + delete fMessenger; } bool StationFinderWindow::QuitRequested() { - messenger->SendMessage(new BMessage(MSG_SEARCH_CLOSE)); + fMessenger->SendMessage(new BMessage(MSG_SEARCH_CLOSE)); return true; } @@ -272,88 +288,100 @@ StationFinderWindow::QuitRequested() void StationFinderWindow::MessageReceived(BMessage* msg) { + switch (msg->what) { case MSG_BNSEARCH: { - if (txSearch->IsEnabled()) { - if (txSearch->Text()[0]) { - DoSearch(txSearch->Text()); - resultView->MakeFocus(true); + if (fTxSearch->IsEnabled()) { + if (fTxSearch->Text()[0]) { + DoSearch(fTxSearch->Text()); + fResultView->MakeFocus(true); } - } else if (kwSearch->Value() >= 0) - DoSearch(currentService->Capability(ddSearchBy->Value()) - ->KeyWords() - ->StringAt(kwSearch->Value())); + } else if (fKwSearch->Value() >= 0) { + DoSearch(fCurrentService->Capability(fDdSearchBy->Value()) + ->KeyWords()->StringAt(fKwSearch->Value())); + } + break; } + case MSG_ADD_STATION: { int32 index = msg->GetInt32("index", -1); if (index < 0) - index = resultView->CurrentSelection(0); + index = fResultView->CurrentSelection(0); + if (index >= 0) { - Station* station = resultView->StationAt(index); + Station* station = fResultView->StationAt(index); + status_t probeStatus = station->Probe(); if (probeStatus == B_OK) { BMessage* dispatch = new BMessage(msg->what); dispatch->AddPointer("station", station); - if (messenger->SendMessage(dispatch) == B_OK) - resultView->RemoveItem(index); + + if (fMessenger->SendMessage(dispatch) == B_OK) + fResultView->RemoveItem(index); } else { BString msg; msg.SetToFormat( B_TRANSLATE("Station %s did not respond correctly and " "could not be added"), station->Name()->String()); + (new BAlert(B_TRANSLATE("Add station failed"), msg, B_TRANSLATE("OK"))) ->Go(); } } + break; } case MSG_UPDATE_STATION: { Station* st = NULL; - status_t status = msg->FindPointer("station", (void**) &st); - if (status == B_OK) - resultView->InvalidateItem( - resultView->IndexOf(resultView->Item(st))); + if (msg->FindPointer("station", (void**)&st) == B_OK) { + fResultView->InvalidateItem( + fResultView->IndexOf(fResultView->Item(st))); + } + break; } case MSG_SELECT_STATION: { bool inResults = (msg->GetInt32("index", -1) >= 0); - bnAdd->SetEnabled(inResults); - bnAdd->MakeDefault(inResults); + fBnAdd->SetEnabled(inResults); + fBnAdd->MakeDefault(inResults); + break; } case MSG_SELECT_SERVICE: - SelectService(ddServices->Value()); + SelectService(fDdServices->Value()); break; case MSG_SEARCH_BY: - SelectCapability(ddSearchBy->Value()); + SelectCapability(fDdSearchBy->Value()); break; case MSG_VISIT_SERVICE: { - if (currentService && currentService->serviceHomePage - && currentService->serviceHomePage.IsValid()) - currentService->serviceHomePage.OpenWithPreferredApplication( + if (fCurrentService != NULL + && fCurrentService->serviceHomePage != NULL + && fCurrentService->serviceHomePage.IsValid()) + fCurrentService->serviceHomePage.OpenWithPreferredApplication( true); break; } + case B_OBSERVER_NOTICE_CHANGE: - bnSearch->MakeDefault(true); + fBnSearch->MakeDefault(true); break; case MSG_TXSEARCH: - bnAdd->MakeDefault(false); - bnSearch->MakeDefault(true); + fBnAdd->MakeDefault(false); + fBnSearch->MakeDefault(true); break; default: @@ -365,24 +393,31 @@ StationFinderWindow::MessageReceived(BMessage* msg) void StationFinderWindow::SelectService(int index) { - if (currentService) { - delete currentService; - currentService = NULL; - } - char* serviceName = StationFinderServices::Name(index); - if (!serviceName) + if (serviceName == NULL) + return; + + StationFinderService* selectedService = StationFinderServices::Instantiate( + serviceName); + if (selectedService == NULL) return; - currentService = StationFinderServices::Instantiate(serviceName); - ((RadioApp*) be_app)->Settings.SetStationFinderName(serviceName); - while (ddSearchBy->CountOptions()) - ddSearchBy->RemoveOptionAt(0); + if (fCurrentService != NULL) { + delete fCurrentService; + fCurrentService = NULL; + } + + fCurrentService = selectedService; + + ((RadioApp*)be_app)->Settings.SetStationFinderName(serviceName); - for (int i = 0; i < currentService->CountCapabilities(); i++) - ddSearchBy->AddOption(currentService->Capability(i)->Name(), i); + while (fDdSearchBy->CountOptions() > 0) + fDdSearchBy->RemoveOptionAt(0); - if (ddSearchBy->CountOptions()) + for (int32 i = 0; i < fCurrentService->CountCapabilities(); i++) + fDdSearchBy->AddOption(fCurrentService->Capability(i)->Name(), i); + + if (fDdSearchBy->CountOptions() != 0) SelectCapability(0); } @@ -390,29 +425,34 @@ StationFinderWindow::SelectService(int index) void StationFinderWindow::SelectCapability(int index) { - if (!currentService) + if (fCurrentService == NULL) return; - FindByCapability* capability = currentService->Capability(index); - if (!capability) + FindByCapability* capability = fCurrentService->Capability(index); + if (capability == NULL) return; if (capability->HasKeyWords()) { - while (kwSearch->CountOptions()) - kwSearch->RemoveOptionAt(0); - for (int i = 0; i < capability->KeyWords()->CountStrings(); i++) - kwSearch->AddOption(capability->KeyWords()->StringAt(i), i); - - searchGrid->RemoveView(txSearch); - searchGrid->AddView(kwSearch, 1, 2); - kwSearch->SetEnabled(true); - txSearch->SetEnabled(false); + while (fKwSearch->CountOptions() > 0) + fKwSearch->RemoveOptionAt(0); + + for (int32 i = 0; i < capability->KeyWords()->CountStrings(); i++) + fKwSearch->AddOption(capability->KeyWords()->StringAt(i), i); + + fSearchGrid->RemoveView(fTxSearch); + fSearchGrid->AddView(fKwSearch, 1, 2); + + fKwSearch->SetEnabled(true); + fTxSearch->SetEnabled(false); + Layout(true); } else { - searchGrid->RemoveView(kwSearch); - searchGrid->AddView(txSearch, 1, 2); - kwSearch->SetEnabled(false); - txSearch->SetEnabled(true); + fSearchGrid->RemoveView(fKwSearch); + fSearchGrid->AddView(fTxSearch, 1, 2); + + fKwSearch->SetEnabled(false); + fTxSearch->SetEnabled(true); + Layout(true); } } @@ -421,20 +461,21 @@ StationFinderWindow::SelectCapability(int index) void StationFinderWindow::DoSearch(const char* text) { - resultView->MakeEmpty(); - if (!currentService) + fResultView->MakeEmpty(); + if (fCurrentService == NULL) return; be_app->SetCursor(new BCursor(B_CURSOR_ID_PROGRESS)); BObjectList* result - = currentService->FindBy(ddSearchBy->Value(), text, this); - if (result) { - for (unsigned i = 0; i < result->CountItems(); i++) - resultView->AddItem(new StationListViewItem(result->ItemAt(i))); + = fCurrentService->FindBy(fDdSearchBy->Value(), text, this); + if (result != NULL) { + for (int32 i = 0; i < result->CountItems(); i++) + fResultView->AddItem(new StationListViewItem(result->ItemAt(i))); + result->MakeEmpty(false); delete result; } - + be_app->SetCursor(B_CURSOR_SYSTEM_DEFAULT); } diff --git a/StationFinder.h b/StationFinder.h index 845a3c5..a90ffd4 100644 --- a/StationFinder.h +++ b/StationFinder.h @@ -1,15 +1,27 @@ /* - * File: StationFinder.h - * Author: Kai Niessen + * Copyright (C) 2017 Kai Niessen * - * Created on 9. Oktober 2015, 22:53 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ +#ifndef _STATION_FINDER_H +#define _STATION_FINDER_H -#ifndef STATIONFINDER_H -#define STATIONFINDER_H -#include "Station.h" -#include "StationListView.h" +#include +#include +#include + #include #include #include @@ -19,9 +31,10 @@ #include #include #include -#include -#include -#include + +#include "Station.h" +#include "StationListView.h" + #define MSG_TXSEARCH 'tSRC' #define MSG_KWSEARCH 'kSRC' @@ -37,100 +50,110 @@ #define RES_BN_SEARCH 10 -using namespace std; - typedef class StationFinderService* (*InstantiateFunc)(); -class StationFinderServices -{ + +class StationFinderServices { public: - StationFinderServices(){}; - ~StationFinderServices(); - static void Register(char* serviceName, InstantiateFunc); - static int CountItems(); - static StationFinderService* Instantiate(char* s); - static char* Name(int i); + StationFinderServices(){}; + ~StationFinderServices(); + + static void Register(char* serviceName, InstantiateFunc); + + static StationFinderService* Instantiate(char* s); + + static int32 CountItems(); + static char* Name(int i); private: - static vector > fServices; + static std::vector > sServices; }; -class FindByCapability -{ +class FindByCapability { public: - FindByCapability(char* name); - FindByCapability(char* name, char* KeyWords, char* delimiter); - ~FindByCapability(); - bool HasKeyWords(); - void SetKeyWords(char* KeyWords, char* delimiter); - const BStringList* KeyWords(); - const char* Name(); + FindByCapability(char* name); + FindByCapability(char* name, char* keyWords, + char* delimiter); + ~FindByCapability(); + + bool HasKeyWords(); + void SetKeyWords(char* keyWords, char* delimiter); + const BStringList* KeyWords(); + + const char* Name(); private: - BString fName; - BStringList fKeywords; + BString fName; + BStringList fKeywords; }; -class StationFinderService -{ +class StationFinderService { friend class StationFinderWindow; public: - // To be overridden in specific StationFinder implementations - StationFinderService(); - virtual ~StationFinderService(); - static void RegisterSelf(); - static StationFinderService* Instantiate(); - virtual BObjectList* FindBy( - int capabilityIndex, const char* searchFor, BLooper* resultUpdateTarget) - = 0; + // Overridden in specific StationFinder implementations + StationFinderService(); + virtual ~StationFinderService(); + + static void RegisterSelf(); + static StationFinderService* Instantiate(); + + virtual BObjectList* FindBy(int capabilityIndex, + const char* searchFor, + BLooper* resultUpdateTarget) = 0; // Provided by ancestor class - const char* Name() const { return serviceName.String(); } - int CountCapabilities() const { return findByCapabilities.CountItems(); }; - FindByCapability* Capability(int index) const - { - return findByCapabilities.ItemAt(index); - }; - static void Register(char* name, InstantiateFunc instantiate); + const char* Name() const { return serviceName.String(); } + + int CountCapabilities() const + { return findByCapabilities.CountItems(); } + FindByCapability* Capability(int index) const + { return findByCapabilities.ItemAt(index); } + + static void Register(char* name, + InstantiateFunc instantiate); protected: - // To be filled by specific StationFinder implementations - BString serviceName; - BUrl serviceHomePage; - BBitmap* serviceLogo; - BObjectList findByCapabilities; - - // Helper functions - BBitmap* logo(BUrl url); - FindByCapability* RegisterSearchCapability(char* name); - FindByCapability* RegisterSearchCapability( - char* name, char* keyWords, char* delimiter); + // To be filled by specific StationFinder implementations + BString serviceName; + BUrl serviceHomePage; + BBitmap* serviceLogo; + BObjectList findByCapabilities; + + // Helper functions + BBitmap* RetrieveLogo(BUrl url); + FindByCapability* RegisterSearchCapability(char* name); + FindByCapability* RegisterSearchCapability(char* name, + char* keyWords, char* delimiter); }; -class StationFinderWindow : public BWindow -{ +class StationFinderWindow : public BWindow { public: - StationFinderWindow(BWindow* parent); - virtual ~StationFinderWindow(); - void MessageReceived(BMessage* msg); - virtual bool QuitRequested(); - void SelectService(int index); - void SelectCapability(int index); - void DoSearch(const char* text); + StationFinderWindow(BWindow* parent); + virtual ~StationFinderWindow(); + + void MessageReceived(BMessage* msg); + virtual bool QuitRequested(); + + void SelectService(int index); + void SelectCapability(int index); + void DoSearch(const char* text); private: - StationFinderService* currentService; - BMessenger* messenger; - BTextControl* txSearch; - BOptionPopUp* kwSearch; - BButton* bnSearch; - StationListView* resultView; - BButton* bnAdd; - BButton* bnVisit; - BOptionPopUp* ddServices; - BOptionPopUp* ddSearchBy; - BGridLayout* searchGrid; + StationFinderService* fCurrentService; + + BMessenger* fMessenger; + BTextControl* fTxSearch; + BOptionPopUp* fKwSearch; + BButton* fBnSearch; + BOptionPopUp* fDdServices; + BButton* fBnVisit; + BOptionPopUp* fDdSearchBy; + StationListView* fResultView; + BButton* fBnAdd; + + BGridLayout* fSearchGrid; }; -#endif /* STATIONFINDER_H */ + +#endif // _STATION_FINDER_H diff --git a/StationFinderListenLive.cpp b/StationFinderListenLive.cpp index 9bb87f8..faf864c 100644 --- a/StationFinderListenLive.cpp +++ b/StationFinderListenLive.cpp @@ -12,27 +12,25 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ -/* - * File: StationFinderListenLive.cpp - * Author: Kai Niessen - * - * Created on March 27, 2017, 12:16 AM - */ #include "StationFinderListenLive.h" + #define __USE_GNU -#include "Debug.h" -#include #include +#include + +#include "Debug.h" + + #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "StationFinderListenLive" -const char* genreList = "Top 40|top40\r\ +const char* kGenreList = "Top 40|top40\r\ Hot Adult Contemporary|hotac\r\ Adult Contemporary|ac\r\ Oldies/80s/90s|oldies\r\ @@ -43,8 +41,7 @@ Rock/Classic Rock|rock\r\ Alternative|alternative\r\ Chillout/Lounge|chillout"; - -const char* countryList = "Andorra|andorra\r\ +const char* kCountryList = "Andorra|andorra\r\ Armenia|armenia\r\ Austria|austria\r\ Azerbaijan|azerbaijan\r\ @@ -96,32 +93,36 @@ Ukraine|ukraine\r\ United Kingdom|uk\r\ Vatican State|vatican"; +const char* StationFinderListenLive::kBaseUrl = "http://listenlive.eu/"; + StationFinderListenLive::StationFinderListenLive() : StationFinderService(), - countryKeywordAndPath(), - genreKeywordAndPath(), - fLookupThread(0), + fCountryKeywordAndPath(), + fGenreKeywordAndPath(), + fLookupThread(-1), fPlsLookupList() { serviceName.SetTo(B_TRANSLATE("listenlive.eu [experimental]")); serviceHomePage.SetUrlString("http://www.listenlive.eu/"); - BString tmp(countryList); - tmp.Split("\r", true, countryKeywordAndPath); + + BString tmp(kCountryList); + tmp.Split("\r", true, fCountryKeywordAndPath); FindByCapability* findByCountry = RegisterSearchCapability("Country"); - BStringList* keywords = (BStringList*) findByCountry->KeyWords(); - for (int i = 0; i < countryKeywordAndPath.CountStrings(); i++) { - BString keyword(countryKeywordAndPath.StringAt(i)); + BStringList* keywords = (BStringList*)findByCountry->KeyWords(); + for (int32 i = 0; i < fCountryKeywordAndPath.CountStrings(); i++) { + BString keyword(fCountryKeywordAndPath.StringAt(i)); keyword.Truncate(keyword.FindFirst('|')); keywords->Add(keyword); } - tmp.SetTo(genreList); - tmp.Split("\r", true, genreKeywordAndPath); + + tmp.SetTo(kGenreList); + tmp.Split("\r", true, fGenreKeywordAndPath); FindByCapability* findByGenre = RegisterSearchCapability("Genre"); - keywords = (BStringList*) findByGenre->KeyWords(); - for (int i = 0; i < genreKeywordAndPath.CountStrings(); i++) { - BString keyword(genreKeywordAndPath.StringAt(i)); + keywords = (BStringList*)findByGenre->KeyWords(); + for (int32 i = 0; i < fGenreKeywordAndPath.CountStrings(); i++) { + BString keyword(fGenreKeywordAndPath.StringAt(i)); keyword.Truncate(keyword.FindFirst('|')); keywords->Add(keyword); } @@ -138,62 +139,64 @@ StationFinderListenLive::Instantiate() void StationFinderListenLive::RegisterSelf() { - // FIXME: "listenlive.eu" no longer seems to exist, though it looks like - // "radiomap.eu" might be its successor? This plugin crashes after searching - // anyway... -// Register( -// "listenlive.eu [experimental]", &StationFinderListenLive::Instantiate); + Register( + "listenlive.eu [experimental]", &StationFinderListenLive::Instantiate); } StationFinderListenLive::~StationFinderListenLive() { fPlsLookupList.MakeEmpty(false); - status_t status; - if (fLookupThread) - wait_for_thread(fLookupThread, &status); + if (fLookupThread >= 0) { + status_t dummy; + wait_for_thread(fLookupThread, &dummy); + } } BObjectList* -StationFinderListenLive::FindBy( - int capabilityIndex, const char* searchFor, BLooper* resultUpdateTarget) +StationFinderListenLive::FindBy(int capabilityIndex, const char* searchFor, + BLooper* resultUpdateTarget) { - if (fLookupThread) + if (fLookupThread >= 0) suspend_thread(fLookupThread); + fPlsLookupList.MakeEmpty(false); fLookupNotify = resultUpdateTarget; BObjectList* result = NULL; - BString urlString(baseUrl); - strlwr((char*) searchFor); + BString urlString(kBaseUrl); + strlwr((char*)searchFor); int keywordIndex = Capability(capabilityIndex)->KeyWords()->IndexOf(searchFor); BStringList* keywordAndPaths - = capabilityIndex ? &genreKeywordAndPath : &countryKeywordAndPath; + = capabilityIndex ? &fGenreKeywordAndPath : &fCountryKeywordAndPath; BString path(keywordAndPaths->StringAt(keywordIndex)); path.Remove(0, path.FindFirst('|') + 1); urlString << path << ".html"; BUrl url(urlString); - BMallocIO* data = HttpUtils::GetAll(url); - if (data) + BMallocIO* data = HttpUtils::GetAll(url); + if (data != NULL) { switch (capabilityIndex) { case 0: // findByCountry result = ParseCountryReturn(data, searchFor); break; + case 1: // findByGenre result = ParseGenreReturn(data, searchFor); break; } + } data->Flush(); delete data; + if (!fPlsLookupList.IsEmpty()) { - if (!fLookupThread) + if (fLookupThread < 0) fLookupThread = spawn_thread( - &PlsLookupFunc, "plslookup", B_NORMAL_PRIORITY, this); + &_PlsLookupFunc, "plslookup", B_NORMAL_PRIORITY, this); resume_thread(fLookupThread); } @@ -206,6 +209,9 @@ StationFinderListenLive::ParseCountryReturn( BMallocIO* data, const char* searchFor) { BObjectList* result = new BObjectList(20, true); + if (result == NULL) + return result; + regmatch_t matches[10]; regex_t regex; @@ -230,7 +236,7 @@ StationFinderListenLive::ParseCountryReturn( RE_ICASE | RE_DOT_NEWLINE | RE_DOT_NOT_NULL | RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_BACKSLASH_ESCAPE_IN_LISTS); - char* doc = (char*) data->Buffer(); + char* doc = (char*)data->Buffer(); off_t size; data->GetSize(&size); doc[size] = 0; @@ -248,6 +254,9 @@ StationFinderListenLive::ParseCountryReturn( doc + matches[6].rm_so, doc + matches[8].rm_so); Station* station = new Station(doc + matches[2].rm_so, NULL); + if (station == NULL) + continue; + result->AddItem(station); BString source(doc + matches[5].rm_so); @@ -257,22 +266,26 @@ StationFinderListenLive::ParseCountryReturn( } else station->SetStreamUrl(BUrl(source)); - for (int i = matches[6].rm_so; i < matches[6].rm_eo; i++) + for (int32 i = matches[6].rm_so; i < matches[6].rm_eo; i++) { if (!strchr("0123456789.", doc[i])) { doc[i] = 0; station->SetBitRate(atof(doc + matches[6].rm_so) * 1000); break; } + } station->SetStation(doc + matches[1].rm_so); station->SetGenre(doc + matches[8].rm_so); + BString country; country.SetTo(searchFor); country.Append(" - "); country.Append(doc + matches[3].rm_so); station->SetCountry(country); + doc += matches[0].rm_eo; } + return result; } @@ -282,6 +295,9 @@ StationFinderListenLive::ParseGenreReturn( BMallocIO* data, const char* searchFor) { BObjectList* result = new BObjectList(20, true); + if (result == NULL) + return result; + regmatch_t matches[9]; regex_t regex; @@ -316,7 +332,7 @@ StationFinderListenLive::ParseGenreReturn( RE_ICASE | RE_DOT_NEWLINE | RE_DOT_NOT_NULL | RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_BACKSLASH_ESCAPE_IN_LISTS); - char* doc = (char*) data->Buffer(); + char* doc = (char*)data->Buffer(); off_t size; data->GetSize(&size); doc[size] = 0; @@ -335,7 +351,11 @@ StationFinderListenLive::ParseGenreReturn( doc + matches[6].rm_so, doc + matches[7].rm_so, searchFor); Station* station = new Station(doc + matches[2].rm_so, NULL); + if (station == NULL) + continue; + result->AddItem(station); + BString source(doc + matches[6].rm_so); if (source.EndsWith(".pls") || source.EndsWith(".m3u")) { station->SetSource(BUrl(source)); @@ -343,47 +363,53 @@ StationFinderListenLive::ParseGenreReturn( } else station->SetStreamUrl(BUrl(source)); - for (int i = matches[7].rm_so; i < matches[7].rm_eo; i++) + for (int32 i = matches[7].rm_so; i < matches[7].rm_eo; i++) { if (!strchr("0123456789.", doc[i])) { doc[i] = 0; station->SetBitRate(atof(doc + matches[7].rm_so) * 1000); break; } + } station->SetStation(doc + matches[1].rm_so); station->SetGenre(searchFor); + BString country(doc + matches[4].rm_so); country.Append(" - "); country.Append(doc + matches[5].rm_so); station->SetCountry(country); + doc += matches[0].rm_eo; } + return result; } int32 -StationFinderListenLive::PlsLookupFunc(void* data) +StationFinderListenLive::_PlsLookupFunc(void* data) { - StationFinderListenLive* _this = (StationFinderListenLive*) data; + StationFinderListenLive* _this = (StationFinderListenLive*)data; while (!_this->fPlsLookupList.IsEmpty()) { Station* station = _this->fPlsLookupList.FirstItem(); TRACE("Looking up stream URL for station %s in %s\n", station->Name()->String(), station->Source().UrlString().String()); + Station* plsStation = Station::LoadIndirectUrl( - (BString&) station->Source().UrlString()); - if (plsStation) + (BString&)station->Source().UrlString()); + if (plsStation != NULL) station->SetStreamUrl(plsStation->StreamUrl()); + BMessage* notification = new BMessage(MSG_UPDATE_STATION); notification->AddPointer("station", station); if (_this->fLookupNotify->LockLooper()) { _this->fLookupNotify->PostMessage(notification); _this->fLookupNotify->UnlockLooper(); } + _this->fPlsLookupList.RemoveItem(station, false); } - _this->fLookupThread = 0; + + _this->fLookupThread = -1; return B_OK; } - -const char* StationFinderListenLive::baseUrl = "http://listenlive.eu/"; diff --git a/StationFinderListenLive.h b/StationFinderListenLive.h index b1e6fb9..662cb3d 100644 --- a/StationFinderListenLive.h +++ b/StationFinderListenLive.h @@ -12,44 +12,47 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ +#ifndef _STATION_FINDER_LISTEN_LIVE_H +#define _STATION_FINDER_LISTEN_LIVE_H -/* - * File: StationFinderListenLive.h - * Author: Kai Niessen - * - * Created on March 27, 2017, 12:16 AM - */ -#ifndef STATIONFINDERLISTENLIVE_H -#define STATIONFINDERLISTENLIVE_H +#include #include "StationFinder.h" -#include -class StationFinderListenLive : public StationFinderService -{ + +class StationFinderListenLive : public StationFinderService { public: - StationFinderListenLive(); - virtual ~StationFinderListenLive(); - static void RegisterSelf(); - static StationFinderService* Instantiate(); - virtual BObjectList* FindBy(int capabilityIndex, - const char* searchFor, BLooper* resultUpdateTarget); - BObjectList* ParseCountryReturn( - BMallocIO* data, const char* country); - BObjectList* ParseGenreReturn(BMallocIO* data, const char* genre); + StationFinderListenLive(); + virtual ~StationFinderListenLive(); + + static void RegisterSelf(); + static StationFinderService* Instantiate(); + + virtual BObjectList* FindBy(int capabilityIndex, + const char* searchFor, + BLooper* resultUpdateTarget); + + BObjectList* ParseCountryReturn(BMallocIO* data, + const char* country); + BObjectList* ParseGenreReturn(BMallocIO* data, + const char* genre); + +private: + static int32 _PlsLookupFunc(void* data); private: - static const char* baseUrl; - thread_id fLookupThread; - BObjectList fPlsLookupList; - BLooper* fLookupNotify; - - static int32 PlsLookupFunc(void* data); - BStringList countryKeywordAndPath; - BStringList genreKeywordAndPath; + static const char* kBaseUrl; + + BStringList fCountryKeywordAndPath; + BStringList fGenreKeywordAndPath; + thread_id fLookupThread; + BObjectList fPlsLookupList; + + BLooper* fLookupNotify; }; -#endif /* STATIONFINDERLISTENLIVE_H */ + +#endif // _STATION_FINDER_LISTEN_LIVE_H diff --git a/StationFinderRadioNetwork.cpp b/StationFinderRadioNetwork.cpp index 2b52552..0ac4fd6 100644 --- a/StationFinderRadioNetwork.cpp +++ b/StationFinderRadioNetwork.cpp @@ -1,19 +1,32 @@ /* - * File: StationFinderRadioNetwork.cpp - * Author: user, Jacob Secunda + * Copyright (C) 2017 Kai Niessen + * Copyright (C) 2020 Jacob Secunda * - * Created on 9. Oktober 2015, 22:51 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + #include "StationFinderRadioNetwork.h" -#include "Debug.h" -#include "HttpUtils.h" -#include "StationFinderListenLive.h" + #include #include #include +#include "Debug.h" +#include "HttpUtils.h" + #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "StationFinderRadioNetwork" @@ -36,7 +49,7 @@ IconLookup::IconLookup(Station* station, BUrl iconUrl) StationFinderRadioNetwork::StationFinderRadioNetwork() : StationFinderService(), - fIconLookupThread(0), + fIconLookupThread(-1), fIconLookupList() { serviceName.SetTo(B_TRANSLATE("Community Radio Browser")); @@ -74,8 +87,8 @@ StationFinderRadioNetwork::RegisterSelf() BObjectList* -StationFinderRadioNetwork::FindBy( - int capabilityIndex, const char* searchFor, BLooper* resultUpdateTarget) +StationFinderRadioNetwork::FindBy(int capabilityIndex, const char* searchFor, + BLooper* resultUpdateTarget) { if (fIconLookupThread) suspend_thread(fIconLookupThread); @@ -90,7 +103,7 @@ StationFinderRadioNetwork::FindBy( if (_CheckServer() != B_OK) return result; - printf("Connected to server: %s \n", sCachedServerUrl.String()); + printf("Connected to server: %s\n", sCachedServerUrl.String()); BString urlString(sCachedServerUrl); @@ -101,24 +114,31 @@ StationFinderRadioNetwork::FindBy( case 0: // Name search urlString.Append("byname/"); break; + case 1: // Tag search urlString.Append("bytag/"); break; + case 2: // Language search urlString.Append("bylanguage/"); break; + case 3: // Country search urlString.Append("bycountry/"); break; + case 4: // Country code search urlString.Append("bycountrycodeexact/"); break; + case 5: // State/Region search urlString.Append("bystate/"); break; + case 6: // Unique identifier search urlString.Append("byuuid/"); break; + default: // A very bad kind of search? Just do a name search... urlString.Append("byname/"); break; @@ -194,9 +214,9 @@ StationFinderRadioNetwork::FindBy( } if (!fIconLookupList.IsEmpty()) { - if (!fIconLookupThread) { + if (fIconLookupThread < 0) { fIconLookupThread = spawn_thread( - &IconLookupFunc, "iconlookup", B_LOW_PRIORITY, this); + &_IconLookupFunc, "iconlookup", B_LOW_PRIORITY, this); } resume_thread(fIconLookupThread); } @@ -210,14 +230,15 @@ StationFinderRadioNetwork::FindBy( int32 -StationFinderRadioNetwork::IconLookupFunc(void* data) +StationFinderRadioNetwork::_IconLookupFunc(void* data) { StationFinderRadioNetwork* _this = (StationFinderRadioNetwork*)data; while (!_this->fIconLookupList.IsEmpty()) { IconLookup* item = _this->fIconLookupList.FirstItem(); - BBitmap* logo = _this->logo(item->fIconUrl); - if (logo) + BBitmap* logo = _this->RetrieveLogo(item->fIconUrl); + if (logo != NULL) item->fStation->SetLogo(logo); + BMessage* notification = new BMessage(MSG_UPDATE_STATION); notification->AddPointer("station", item->fStation); if (_this->fIconLookupNotify->LockLooper()) { @@ -227,7 +248,8 @@ StationFinderRadioNetwork::IconLookupFunc(void* data) _this->fIconLookupList.RemoveItem(item, true); } - _this->fIconLookupThread = 0; + _this->fIconLookupThread = -1; + return B_OK; } diff --git a/StationFinderRadioNetwork.h b/StationFinderRadioNetwork.h index 7535e59..5849cd9 100644 --- a/StationFinderRadioNetwork.h +++ b/StationFinderRadioNetwork.h @@ -1,43 +1,60 @@ /* - * File: StationFinderRadioNetwork.h - * Author: user, Jacob Secunda + * Copyright (C) 2017 Kai Niessen + * Copyright (C) 2020 Jacob Secunda * - * Created on 9. Oktober 2015, 22:51 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ +#ifndef _STATION_FINDER_RADIO_NETWORK_H +#define _STATION_FINDER_RADIO_NETWORK_H -#ifndef STATIONFINDERRADIONETWORK_H -#define STATIONFINDERRADIONETWORK_H #include "StationFinder.h" -class IconLookup -{ + +class IconLookup { public: - IconLookup(Station* station, BUrl iconUrl); - Station* fStation; - BUrl fIconUrl; + IconLookup(Station* station, BUrl iconUrl); + + Station* fStation; + BUrl fIconUrl; }; -class StationFinderRadioNetwork : public StationFinderService -{ + +class StationFinderRadioNetwork : public StationFinderService { public: - StationFinderRadioNetwork(); - virtual ~StationFinderRadioNetwork(); - static void RegisterSelf(); - static StationFinderService* Instantiate(); - virtual BObjectList* FindBy(int capabilityIndex, - const char* searchFor, BLooper* resultUpdateTarget); + StationFinderRadioNetwork(); + virtual ~StationFinderRadioNetwork(); + + static void RegisterSelf(); + static StationFinderService* Instantiate(); + + virtual BObjectList* FindBy(int capabilityIndex, + const char* searchFor, + BLooper* resultUpdateTarget); private: - static const char* kBaseUrl; - static BString sCachedServerUrl; + static int32 _IconLookupFunc(void* data); + status_t _CheckServer(); - thread_id fIconLookupThread; - BObjectList fIconLookupList; - BLooper* fIconLookupNotify; +private: + static const char* kBaseUrl; + static BString sCachedServerUrl; - static int32 IconLookupFunc(void* data); - status_t _CheckServer(); + thread_id fIconLookupThread; + BObjectList fIconLookupList; + BLooper* fIconLookupNotify; }; -#endif /* STATIONFINDERRADIONETWORK_H */ + +#endif // _STATION_FINDER_RADIO_NETWORK_H diff --git a/StationListView.cpp b/StationListView.cpp index ae17b74..60bbb95 100644 --- a/StationListView.cpp +++ b/StationListView.cpp @@ -1,16 +1,31 @@ /* - * File: StationListView.cpp - * Author: user + * Copyright (C) 2017 Kai Niessen * - * Created on 26.02.2013, 09:22 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + + #include "StationListView.h" -#include "StreamPlayer.h" -#include "Utils.h" + #include #include #include +#include "StreamPlayer.h" +#include "Utils.h" + + #define SLV_INSET 2 #define SLV_PADDING 6 #define SLV_HEIGHT 50 @@ -31,7 +46,7 @@ StationListViewItem::StationListViewItem(Station* station) StationListViewItem::~StationListViewItem() { - if (fPlayer) { + if (fPlayer != NULL) { fPlayer->Stop(); delete fPlayer; } @@ -41,7 +56,7 @@ StationListViewItem::~StationListViewItem() StreamPlayer::PlayState StationListViewItem::State() { - return (fPlayer) + return (fPlayer != NULL) ? fPlayer->State() : fStation->Flags(STATION_URI_VALID) ? StreamPlayer::Stopped : StreamPlayer::InActive; @@ -51,8 +66,9 @@ StationListViewItem::State() void StationListViewItem::DrawItem(BView* owner, BRect frame, bool complete) { - int index = ((BListView*) owner)->IndexOf(this); - StationListView* ownerList = (StationListView*) owner; + int index = ((BListView*)owner)->IndexOf(this); + + StationListView* ownerList = (StationListView*)owner; ownerList->SetHighColor( ui_color(IsSelected() ? B_MENU_SELECTION_BACKGROUND_COLOR : ((index % 2) ? B_MENU_BACKGROUND_COLOR @@ -65,17 +81,22 @@ StationListViewItem::DrawItem(BView* owner, BRect frame, bool complete) ui_color(IsSelected() ? B_MENU_SELECTION_BACKGROUND_COLOR : ((index % 2) ? B_MENU_BACKGROUND_COLOR : B_DOCUMENT_BACKGROUND_COLOR))); - if (BBitmap* logo = fStation->Logo()) { + + if (fStation->Logo() != NULL) { BRect target(SLV_INSET, SLV_INSET, SLV_HEIGHT - 2 * SLV_INSET, SLV_HEIGHT - 2 * SLV_INSET); target.OffsetBy(frame.LeftTop()); + owner->DrawBitmap( - logo, logo->Bounds(), target, B_FILTER_BITMAP_BILINEAR); + fStation->Logo(), fStation->Logo()->Bounds(), target, + B_FILTER_BITMAP_BILINEAR); } + owner->SetFontSize(SLV_MAIN_FONT_SIZE); font_height fontHeight; owner->GetFontHeight(&fontHeight); float baseline = SLV_INSET + fontHeight.ascent + fontHeight.leading; + owner->DrawString(fStation->Name()->String(), frame.LeftTop() + BPoint( @@ -96,12 +117,11 @@ StationListViewItem::DrawItem(BView* owner, BRect frame, bool complete) frame = ownerList->ItemFrame(ownerList->StationIndex(fStation)); if (ownerList->CanPlay() && fStation->Flags(STATION_URI_VALID)) { - BBitmap* bnBitmap = getButtonBitmap(State()); + BBitmap* bnBitmap = _GetButtonBitmap(State()); if (bnBitmap != NULL) { owner->SetDrawingMode(B_OP_ALPHA); - owner->DrawBitmap(bnBitmap, - frame.LeftTop() - + BPoint(SLV_INSET, SLV_HEIGHT - SLV_INSET - SLV_BUTTON_SIZE)); + owner->DrawBitmap(bnBitmap, frame.LeftTop() + + BPoint(SLV_INSET, SLV_HEIGHT - SLV_INSET - SLV_BUTTON_SIZE)); owner->SetDrawingMode(B_OP_COPY); } } @@ -117,10 +137,12 @@ StationListViewItem::DrawBufferFillBar(BView* owner, BRect frame) frame.left = SLV_HEIGHT - SLV_INSET + SLV_PADDING; frame.top = frame.bottom - 5; frame.right -= SLV_PADDING; + owner->SetHighColor( tint_color(ui_color(B_MENU_BACKGROUND_COLOR), B_LIGHTEN_2_TINT)); owner->FillRect(frame); owner->SetHighColor(200, 30, 0); + frame.right = frame.left + frame.Width() * fFillRatio; owner->FillRect(frame); } @@ -137,24 +159,26 @@ StationListViewItem::Update(BView* owner, const BFont* font) void StationListViewItem::StateChanged(StreamPlayer::PlayState newState) { - if (fList && fList->LockLooperWithTimeout(0) == B_OK) { + if (fList != NULL && fList->LockLooperWithTimeout(0) == B_OK) { fList->InvalidateItem(fList->IndexOf(this)); fList->UnlockLooper(); } } +BBitmap* StationListViewItem::sButtonBitmap[3] = {NULL, NULL, NULL}; + BBitmap* -StationListViewItem::getButtonBitmap(StreamPlayer::PlayState state) +StationListViewItem::_GetButtonBitmap(StreamPlayer::PlayState state) { if (state < 0) return NULL; - if (buttonBitmap[state] == NULL) - buttonBitmap[state] = Utils::ResourceBitmap(RES_BN_STOPPED + state); - return buttonBitmap[state]; -} -BBitmap* StationListViewItem::buttonBitmap[3] = {NULL, NULL, NULL}; + if (sButtonBitmap[state] == NULL) + sButtonBitmap[state] = Utils::ResourceBitmap(RES_BN_STOPPED + state); + + return sButtonBitmap[state]; +} void @@ -176,13 +200,13 @@ StationListView::StationListView(bool canPlay) { SetResizingMode(B_FOLLOW_ALL_SIDES); SetExplicitMinSize(BSize(300, 2 * SLV_HEIGHT)); - playMsg = NULL; + fPlayMsg = NULL; } StationListView::~StationListView() { - delete playMsg; + delete fPlayMsg; } @@ -204,9 +228,11 @@ StationListView::AddItem(Station* station) int32 StationListView::StationIndex(Station* station) { - for (int32 i = 0; i < CountItems(); i++) - if (((StationListViewItem*) ItemAt(i))->GetStation() == station) + for (int32 i = 0; i < CountItems(); i++) { + if (((StationListViewItem*)ItemAt(i))->GetStation() == station) return i; + } + return -1; } @@ -214,7 +240,7 @@ StationListView::StationIndex(Station* station) StationListViewItem* StationListView::ItemAt(int32 index) { - return (StationListViewItem*) BListView::ItemAt(index); + return (StationListViewItem*)BListView::ItemAt(index); } @@ -222,7 +248,7 @@ Station* StationListView::StationAt(int32 index) { StationListViewItem* item = ItemAt(index); - return (item) ? item->GetStation() : NULL; + return (item != NULL) ? item->GetStation() : NULL; } @@ -230,10 +256,11 @@ StationListViewItem* StationListView::Item(Station* station) { for (int32 i = 0; i < CountItems(); i++) { - StationListViewItem* stationItem = (StationListViewItem*) ItemAt(i); + StationListViewItem* stationItem = (StationListViewItem*)ItemAt(i); if (stationItem->GetStation() == station) return stationItem; } + return NULL; } @@ -242,36 +269,39 @@ void StationListView::Sync(StationsList* stations) { LockLooper(); + for (int32 i = CountItems() - 1; i >= 0; i--) { StationListViewItem* stationItem = (StationListViewItem*) ItemAt(i); if (!stations->HasItem(stationItem->GetStation())) { RemoveItem(i); delete stationItem; - } } + } for (int32 i = 0; i < stations->CountItems(); i++) { Station* station = stations->ItemAt(i); if (Item(station) == NULL) AddItem(station); } + Invalidate(); + UnlockLooper(); } void -StationListView::SetPlayMessage(BMessage* playMsg) +StationListView::SetPlayMessage(BMessage* fPlayMsg) { - delete this->playMsg; - this->playMsg = playMsg; + delete this->fPlayMsg; + this->fPlayMsg = fPlayMsg; } void StationListView::MouseDown(BPoint where) { - whereDown = where; + fWhereDown = where; BListView::MouseDown(where); } @@ -279,18 +309,22 @@ StationListView::MouseDown(BPoint where) void StationListView::MouseUp(BPoint where) { - whereDown -= where; - if (whereDown.x * whereDown.x + whereDown.y * whereDown.y < 5 && playMsg) { + fWhereDown -= where; + if (fWhereDown.x * fWhereDown.x + fWhereDown.y * fWhereDown.y < 5 + && fPlayMsg != NULL) { int stationIndex = IndexOf(where); + BRect playFrame = ItemFrame(stationIndex); playFrame.InsetBy(SLV_INSET, SLV_INSET); playFrame.right = playFrame.left + SLV_BUTTON_SIZE; playFrame.top = playFrame.bottom - SLV_BUTTON_SIZE; + if (playFrame.Contains(where)) { - BMessage* clone = new BMessage(playMsg->what); + BMessage* clone = new BMessage(fPlayMsg->what); clone->AddInt32("index", stationIndex); InvokeNotify(clone); } } - whereDown = BPoint(-1, -1); + + fWhereDown = BPoint(-1, -1); } diff --git a/StationListView.h b/StationListView.h index 67ed376..511f8be 100644 --- a/StationListView.h +++ b/StationListView.h @@ -1,12 +1,23 @@ /* - * File: StationListView.h - * Author: user + * Copyright (C) 2017 Kai Niessen * - * Created on 26. Februar 2013, 09:22 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ +#ifndef _STATION_LIST_VIEW_H +#define _STATION_LIST_VIEW_H + -#ifndef STATIONLISTVIEW_H -#define STATIONLISTVIEW_H #include #include #include @@ -16,58 +27,75 @@ #include "Station.h" #include "StreamPlayer.h" + #define MSG_STATION_LIST 'STLS' + class StationListView; -class StationListViewItem : public BListItem -{ + +class StationListViewItem : public BListItem { friend class StationListView; public: - StationListViewItem(Station* station); - virtual ~StationListViewItem(); - virtual void DrawItem(BView* owner, BRect frame, bool complete); - void DrawBufferFillBar(BView* owner, BRect frame); - virtual void Update(BView* owner, const BFont* font); - Station* GetStation() { return fStation; } - void StateChanged(StreamPlayer::PlayState newState); - void SetFillRatio(float fillRatio); - StreamPlayer* Player() { return fPlayer; } - StreamPlayer::PlayState State(); - void SetPlayer(StreamPlayer* player) { fPlayer = player; } + StationListViewItem(Station* station); + virtual ~StationListViewItem(); + + virtual void DrawItem(BView* owner, BRect frame, + bool complete); + + void DrawBufferFillBar(BView* owner, BRect frame); + virtual void Update(BView* owner, const BFont* font); + Station* GetStation() { return fStation; } + void StateChanged(StreamPlayer::PlayState newState); + void SetFillRatio(float fillRatio); + + StreamPlayer* Player() { return fPlayer; } + StreamPlayer::PlayState State(); + void SetPlayer(StreamPlayer* player) + { fPlayer = player; } + +private: + static BBitmap* _GetButtonBitmap(StreamPlayer::PlayState state); private: - StationListView* fList; - class Station* fStation; - StreamPlayer* fPlayer; - float fFillRatio; - static BBitmap* getButtonBitmap(StreamPlayer::PlayState state); - static BBitmap* buttonBitmap[3]; + static BBitmap* sButtonBitmap[3]; + + StreamPlayer* fPlayer; + class Station* fStation; + StationListView* fList; + + float fFillRatio; }; -class StationListView : public BListView -{ + +class StationListView : public BListView { public: - StationListView(bool canPlay = false); - virtual ~StationListView(); - void Sync(StationsList* stations); - virtual bool AddItem(Station* station); - virtual bool AddItem(StationListViewItem* item); - int32 StationIndex(Station* station); - StationListViewItem* Item(Station* station); - StationListViewItem* ItemAt(int32 index); - Station* StationAt(int32 index); - void SetPlayMessage(BMessage* playMsg); - bool CanPlay() { return fCanPlay; } + StationListView(bool canPlay = false); + virtual ~StationListView(); + + void Sync(StationsList* stations); + + virtual bool AddItem(Station* station); + virtual bool AddItem(StationListViewItem* item); + + int32 StationIndex(Station* station); + StationListViewItem* Item(Station* station); + StationListViewItem* ItemAt(int32 index); + Station* StationAt(int32 index); + + void SetPlayMessage(BMessage* playMsg); + bool CanPlay() { return fCanPlay; } + +private: + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); private: - virtual void MouseDown(BPoint where); - virtual void MouseUp(BPoint where); - BPoint whereDown; - BMessage* playMsg; - bool fCanPlay; + BPoint fWhereDown; + BMessage* fPlayMsg; + bool fCanPlay; }; -#endif /* STATIONLISTVIEW_H */ +#endif // _STATION_LIST_VIEW_H diff --git a/StationPanel.cpp b/StationPanel.cpp index 08f9e5c..a45c50d 100644 --- a/StationPanel.cpp +++ b/StationPanel.cpp @@ -12,27 +12,23 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ -/* - * File: StationPanel.cpp - * Author: Kai Niessen - * - * Created on April 13, 2017, 1:25 PM - */ -#include - -#include "Debug.h" -#include "MainWindow.h" #include "StationPanel.h" -#include "Utils.h" + #include #include #include +#include #include +#include "Debug.h" +#include "MainWindow.h" +#include "Utils.h" + + #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "StationPanel" @@ -46,24 +42,32 @@ StationPanel::StationPanel(MainWindow* mainWindow, bool expanded) SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM); SetExplicitAlignment( BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_VERTICAL_UNSET)); + fLogo = new BView("logo", B_WILL_DRAW); fLogo->SetExplicitSize(BSize(96, 96)); fLogo->SetViewColor( tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_2_TINT)); + fName = new BTextControl( "name", B_TRANSLATE("Name"), "-", new BMessage(MSG_CHG_NAME)); + fUrl = new BTextControl( "url", B_TRANSLATE("Stream URL"), "-", new BMessage(MSG_CHG_STREAMURL)); + fGenre = new BTextControl( "genre", B_TRANSLATE("Genre"), "-", new BMessage(MSG_CHG_GENRE)); + fStationUrl = new BTextControl("surl", B_TRANSLATE("Station URL"), "-", new BMessage(MSG_CHG_STATIONURL)); + fVisitStation = new BButton("bnVisitStation", "", new BMessage(MSG_VISIT_STATION)); fVisitStation->SetIcon(Utils::ResourceBitmap(RES_BN_WEB)); + fVolume = new BSlider( "volume", NULL, new BMessage(MSG_CHG_VOLUME), 0, 100, B_VERTICAL); fVolume->SetModificationMessage(new BMessage(MSG_CHG_VOLUME)); + BLayoutBuilder::Grid<>(this, 7, 3) .SetInsets(B_USE_SMALL_INSETS) .SetSpacing(B_USE_ITEM_SPACING, B_USE_SMALL_SPACING) @@ -74,7 +78,7 @@ StationPanel::StationPanel(MainWindow* mainWindow, bool expanded) .AddTextControl(fStationUrl, 1, 2, B_ALIGN_LEFT, 1, 3) .Add(fVisitStation, 5, 2) .Add(fVolume, 6, 0, 1, 3) - .End(); + .End(); } @@ -93,6 +97,7 @@ StationPanel::AttachedToWindow() fStationUrl->SetTarget(this); fVisitStation->SetTarget(this); fVolume->SetTarget(this); + SetStation(NULL); } @@ -103,39 +108,53 @@ StationPanel::SetStation(StationListViewItem* stationItem) if (stationItem == NULL) { fName->SetText(NULL); fName->SetEnabled(false); + fGenre->SetText(NULL); fGenre->SetEnabled(false); + fUrl->SetText(NULL); fUrl->SetEnabled(false); + fStationUrl->SetText(NULL); fStationUrl->SetEnabled(false); + fVisitStation->SetEnabled(false); + fLogo->ClearViewBitmap(); + fVolume->SetValue(0); fVolume->SetEnabled(false); + fStationItem = NULL; } else { - if (stationItem->Player() + if (stationItem->Player() != NULL && stationItem->Player()->State() == StreamPlayer::Playing) { fVolume->SetEnabled(true); fVolume->SetValue(stationItem->Player()->Volume() * 100); } else fVolume->SetEnabled(false); + fStationItem = stationItem; + Station* station = stationItem->GetStation(); - if (station->Logo()) + if (station->Logo()) { fLogo->SetViewBitmap(station->Logo(), station->Logo()->Bounds(), fLogo->Bounds(), 0, B_FILTER_BITMAP_BILINEAR); - else + } else fLogo->ClearViewBitmap(); + fName->SetEnabled(true); fName->SetText(station->Name()->String()); + fGenre->SetEnabled(true); fGenre->SetText(station->Genre().String()); + fUrl->SetEnabled(true); fUrl->SetText(station->StreamUrl().UrlString()); + fStationUrl->SetEnabled(true); fStationUrl->SetText(station->StationUrl().UrlString()); + fVisitStation->SetEnabled(true); } } @@ -144,7 +163,7 @@ StationPanel::SetStation(StationListViewItem* stationItem) void StationPanel::StateChanged(StreamPlayer::PlayState newState) { - if (fStationItem && fStationItem->Player() + if (fStationItem != NULL && fStationItem->Player() != NULL && fStationItem->Player()->State() == StreamPlayer::Playing) { fVolume->SetEnabled(true); fVolume->SetValue(fStationItem->Player()->Volume() * 100); @@ -160,49 +179,62 @@ StationPanel::MessageReceived(BMessage* msg) { Station* station = (fStationItem != NULL) ? fStationItem->GetStation() : NULL; - if (station) { + if (station != NULL) { entry_ref ref; - BBitmap* bm; if (msg->WasDropped() && msg->IsSourceRemote() - && msg->FindRef("refs", &ref) == B_OK - && (bm = BTranslationUtils::GetBitmap(&ref))) { - station->SetLogo(bm); - fLogo->SetViewBitmap(station->Logo(), station->Logo()->Bounds(), - fLogo->Bounds(), 0, B_FILTER_BITMAP_BILINEAR); + && msg->FindRef("refs", &ref) == B_OK) { + BBitmap* bm = BTranslationUtils::GetBitmap(&ref); + if (bm != NULL) { + station->SetLogo(bm); + fLogo->SetViewBitmap(station->Logo(), station->Logo()->Bounds(), + fLogo->Bounds(), 0, B_FILTER_BITMAP_BILINEAR); + } + return; } + switch (msg->what) { case MSG_CHG_VOLUME: { StreamPlayer* player = fStationItem->Player(); - if (player) + if (player != NULL) player->SetVolume(fVolume->Value() / 100.0); + break; } + case MSG_CHG_NAME: station->SetName(fName->Text()); break; + case MSG_CHG_STREAMURL: station->SetStreamUrl(fUrl->Text()); break; + case MSG_CHG_GENRE: station->SetGenre(fGenre->Text()); break; + case MSG_CHG_STATIONURL: station->SetStation(fStationUrl->Text()); break; + case MSG_VISIT_STATION: + { MSG("Trying to launch url %s\n", station->StationUrl().UrlString().String()); + if (station->StationUrl().UrlString().IsEmpty()) { BString search; - search.SetToFormat("http://google.com/search?q=%s", + search.SetToFormat("https://google.com/search?q=%s", BUrl::UrlEncode(*station->Name()).String()); BUrl searchUrl(search); searchUrl.OpenWithPreferredApplication(false); } else station->StationUrl().OpenWithPreferredApplication(false); + break; + } default: BView::MessageReceived(msg); diff --git a/StationPanel.h b/StationPanel.h index 10244c6..7d89361 100644 --- a/StationPanel.h +++ b/StationPanel.h @@ -12,26 +12,21 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ +#ifndef _STATION_PANEL_H +#define _STATION_PANEL_H -/* - * File: StationPanel.h - * Author: Kai Niessen - * - * Created on April 13, 2017, 1:25 PM - */ - -#ifndef STATIONPANEL_H -#define STATIONPANEL_H -#include "Station.h" -#include "StationListView.h" #include #include #include #include +#include "Station.h" +#include "StationListView.h" + + #define MSG_CHG_NAME 'cNAM' #define MSG_CHG_VOLUME 'cVOL' #define MSG_CHG_GENRE 'cGNR' @@ -39,28 +34,34 @@ #define MSG_CHG_STATIONURL 'cSUR' #define MSG_VISIT_STATION 'vSUR' + class MainWindow; -class StationPanel : public BView -{ + +class StationPanel : public BView { public: - StationPanel(MainWindow* mainWindow, bool expanded = false); - virtual ~StationPanel(); - virtual void AttachedToWindow(); - virtual void MessageReceived(BMessage* msg); - void SetStation(StationListViewItem* stationItem); - void StateChanged(StreamPlayer::PlayState newState); + StationPanel(MainWindow* mainWindow, + bool expanded = false); + virtual ~StationPanel(); + + virtual void AttachedToWindow(); + virtual void MessageReceived(BMessage* msg); + + void SetStation(StationListViewItem* stationItem); + void StateChanged(StreamPlayer::PlayState newState); private: - StationListViewItem* fStationItem; - MainWindow* fMainWindow; - BView* fLogo; - BTextControl* fName; - BTextControl* fUrl; - BTextControl* fGenre; - BTextControl* fStationUrl; - BButton* fVisitStation; - BSlider* fVolume; + StationListViewItem* fStationItem; + MainWindow* fMainWindow; + + BView* fLogo; + BTextControl* fName; + BTextControl* fUrl; + BTextControl* fGenre; + BTextControl* fStationUrl; + BButton* fVisitStation; + BSlider* fVolume; }; -#endif /* STATIONPANEL_H */ + +#endif // _STATION_PANEL_H diff --git a/StreamIO.cpp b/StreamIO.cpp index c220cf9..3921fa0 100644 --- a/StreamIO.cpp +++ b/StreamIO.cpp @@ -12,33 +12,34 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ -/* - * File: StreamIO.cpp - * Author: Kai Niessen - * - * Created on April 11, 2017, 10:21 PM - */ #include "StreamIO.h" -#include "Debug.h" -#include "HttpUtils.h" -#include "MediaIO.h" -#include "Station.h" + #define __USE_GNU +#include + #include #include -#include + +#include "Debug.h" +#include "HttpUtils.h" +#include "Station.h" + + +const char kMpegHeader1 = '\xff'; +const char kMpegHeader2 = '\xe0'; #define HTTP_TIMEOUT 30000000 + #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "StreamIO" -StreamIO::StreamIO(Station* station, BLooper* MetaListener) +StreamIO::StreamIO(Station* station, BLooper* metaListener) : BAdapterIO(B_MEDIA_STREAMING | B_MEDIA_SEEKABLE, HTTP_TIMEOUT), fStation(station), @@ -50,11 +51,10 @@ StreamIO::StreamIO(Station* station, BLooper* MetaListener) fMetaSize(0), fUntilMetaStart(0), fUntilMetaEnd(0), - fMetaListener(MetaListener), + fMetaListener(metaListener), fFrameSync(none), fLimit(0), fBuffered(0) - { BUrl url = station->StreamUrl(); @@ -76,33 +76,41 @@ StreamIO::StreamIO(Station* station, BLooper* MetaListener) // of streams using HTTPS and load balancing between two or more different // IP's should be small, anyway. - if (url.Protocol() == "https") + if (url.Protocol() == "https") { fReq = dynamic_cast( BUrlProtocolRoster::MakeRequest(url.UrlString().String(), this)); - else { + } else { BUrl* newUrl = new BUrl(); + if (newUrl == NULL) + return; + status_t portStatus = HttpUtils::CheckPort(url, newUrl); if (portStatus != B_OK) return; + fReq = dynamic_cast(BUrlProtocolRoster::MakeRequest( newUrl->UrlString().String(), this)); delete newUrl; } BHttpHeaders* headers = new BHttpHeaders(); + if (headers == NULL) + return; + headers->AddHeader("Icy-MetaData", 1); headers->AddHeader("Icy-Reset", 1); headers->AddHeader("Accept", "audio/*"); + fReq->AdoptHeaders(headers); fReq->SetFollowLocation(true); fReq->SetMaxRedirections(3); + fInputAdapter = BuildInputAdapter(); const char* mime = fStation->Mime()->Type(); if (!strcmp(mime, "audio/mpeg") || !strcmp(mime, "audio/aacp")) fDataFuncs.Add(&StreamIO::DataUnsyncedReceived); - fDataFuncs.Add(&StreamIO::DataSyncedReceived); } @@ -114,6 +122,7 @@ StreamIO::~StreamIO() status_t status; wait_for_thread(fReqThread, &status); } + delete fReq; } @@ -143,16 +152,18 @@ StreamIO::ReadAt(off_t position, void* buffer, size_t size) " remaining\n", read, size, position, Buffered()); fBuffered -= read; - } else + } else { TRACE("Reading %" B_PRIuSIZE " bytes from position %" B_PRIdOFF " failed - %s\n", size, position, strerror(read)); + } return read; } else { - TRACE("Position %" B_PRIuSIZE " has reached limit of %" B_PRIuSIZE + TRACE("Position %" B_PRIdOFF " has reached limit of %" B_PRIuSIZE ", blocking...\n", position, fLimit); + return 0; } } @@ -165,20 +176,6 @@ StreamIO::SetSize(off_t size) } -size_t -StreamIO::Buffered() -{ - return fBuffered; -} - - -void -StreamIO::SetLimiter(size_t limit) -{ - fLimit = limit; -} - - status_t StreamIO::Open() { @@ -187,6 +184,7 @@ StreamIO::Open() return B_ERROR; fReqStartTime = system_time() + HTTP_TIMEOUT; + return BAdapterIO::Open(); } @@ -198,6 +196,20 @@ StreamIO::IsRunning() const } +void +StreamIO::SetLimiter(size_t limit) +{ + fLimit = limit; +} + + +size_t +StreamIO::Buffered() +{ + return fBuffered; +} + + status_t StreamIO::SeekRequested(off_t position) { @@ -227,6 +239,7 @@ StreamIO::HeadersReceived(BUrlRequest* request, const BUrlResult& result) httpResult->Headers()["location"]); } else TRACE("Redirected to %s\n", httpResult->Headers()["location"]); + return; } @@ -240,8 +253,8 @@ StreamIO::HeadersReceived(BUrlRequest* request, const BUrlResult& result) fIsMutable = true; const char* sMetaInt = httpResult->Headers()["icy-metaint"]; - icyName = httpResult->Headers()["icy-name"]; - if (sMetaInt) { + fIcyName = httpResult->Headers()["icy-name"]; + if (sMetaInt != NULL) { fMetaInt = atoi(sMetaInt); fUntilMetaStart = fMetaInt; fDataFuncs.Add(&StreamIO::DataWithMetaReceived, 0); @@ -270,10 +283,11 @@ void StreamIO::DataWithMetaReceived(BUrlRequest* request, const char* data, off_t position, ssize_t size, int next) { - while (size) - if (fUntilMetaEnd != 0) + while (size > 0) { + if (fUntilMetaEnd != 0) { if (fUntilMetaEnd <= size) { - memcpy(fMetaBuffer + fMetaSize, (void*) data, fUntilMetaEnd); + memcpy(fMetaBuffer + fMetaSize, (void*)data, fUntilMetaEnd); + data += fUntilMetaEnd; size -= fUntilMetaEnd; fMetaSize += fUntilMetaEnd; @@ -292,7 +306,7 @@ StreamIO::DataWithMetaReceived(BUrlRequest* request, const char* data, fUntilMetaEnd -= size; size = 0; } - else { + } else { DataFunc nextFunc = fDataFuncs.Item(next); if (size <= fUntilMetaStart) { (*this.*nextFunc)(request, data, position, size, next + 1); @@ -304,6 +318,7 @@ StreamIO::DataWithMetaReceived(BUrlRequest* request, const char* data, size -= fUntilMetaStart; position += fUntilMetaStart; data += fUntilMetaStart; + fUntilMetaStart = 0; fUntilMetaEnd = *data * 16; if (fUntilMetaEnd == 0) { @@ -312,52 +327,54 @@ StreamIO::DataWithMetaReceived(BUrlRequest* request, const char* data, } else if (fUntilMetaEnd > 512) { TRACE("Meta: Size of %" B_PRIdOFF " too large\n", fUntilMetaEnd); + fUntilMetaStart = fMetaInt; fUntilMetaEnd = 0; data--; size++; } + data++; size--; } } + } } -const char mpegHeader1 = '\xff'; -const char mpegHeader2 = '\xe0'; - void StreamIO::DataUnsyncedReceived(BUrlRequest* request, const char* data, off_t position, ssize_t size, int next) { off_t frameStart; - for (frameStart = 0; frameStart < size - 1; frameStart++) + for (frameStart = 0; frameStart < size - 1; frameStart++) { if (fFrameSync == none) { - if (data[frameStart] == mpegHeader1) { + if (data[frameStart] == kMpegHeader1) { fFrameSync = first; continue; } - } else if ((data[frameStart] & mpegHeader2) == mpegHeader2) { + } else if ((data[frameStart] & kMpegHeader2) == kMpegHeader2) { next--; fDataFuncs.Remove(next); DataFunc nextFunc = fDataFuncs.Item(next); (*this.*nextFunc)( - request, &mpegHeader1, position + frameStart - 1, 1, next + 1); + request, &kMpegHeader1, position + frameStart - 1, 1, next + 1); (*this.*nextFunc)(request, data + frameStart, position + frameStart, size - frameStart, next + 1); return; } else fFrameSync = none; + } if (position + frameStart > 8192) { - MSG("No mp3 frame header encountered in first %" B_PRIuSIZE + MSG("No mp3 frame header encountered in first %" B_PRIiOFF " bytes, giving up...", position + frameStart); + next--; fDataFuncs.Remove(next); - DataFunc nextFunc = fDataFuncs.Item(next); + // DataFunc nextFunc = fDataFuncs.Item(next); } } @@ -378,17 +395,21 @@ StreamIO::RequestCompleted(BUrlRequest* request, bool success) fReqThread = -1; } -// void -// StreamIO::DebugMessage(BUrlRequest* caller, BUrlProtocolDebugMessage type, -// const char* text) { MSG("Debug: %s\n", text); -//} + +void +StreamIO::DebugMessage(BUrlRequest* caller, BUrlProtocolDebugMessage type, + const char* text) +{ + DEBUG("Debug Message: %s\n", text); +} + void StreamIO::ProcessMeta() { TRACE("Meta: %s\n", fMetaBuffer); - if (!fMetaListener) + if (fMetaListener == NULL) return; regmatch_t matches[4]; @@ -396,19 +417,24 @@ StreamIO::ProcessMeta() BMessage* msg = new BMessage(MSG_META_CHANGE); msg->AddString("station", fStation->Name()->String()); + int res = regcomp(®ex, "([^=]*)='(([^']|'[^;])*)';", RE_ICASE | RE_DOT_NEWLINE | RE_DOT_NOT_NULL | RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_BACKSLASH_ESCAPE_IN_LISTS); + char* text = fMetaBuffer; while ((res = regexec(®ex, text, 3, matches, 0)) == 0) { text[matches[1].rm_eo] = 0; text[matches[2].rm_eo] = 0; + if (text + matches[2].rm_so && !(text + matches[2].rm_so)[0]) - msg->AddString(strlwr(text + matches[1].rm_so), icyName); + msg->AddString(strlwr(text + matches[1].rm_so), fIcyName); else msg->AddString( strlwr(text + matches[1].rm_so), text + matches[2].rm_so); + text += matches[0].rm_eo; } + fMetaListener->PostMessage(msg); } diff --git a/StreamIO.h b/StreamIO.h index c1ee6e8..7b2cb7e 100644 --- a/StreamIO.h +++ b/StreamIO.h @@ -12,125 +12,160 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ +#ifndef _STREAM_IO_H +#define _STREAM_IO_H -/* - * File: StreamIO.h - * Author: Kai Niessen - * - * Created on April 11, 2017, 10:21 PM - */ -#ifndef STREAMIO_H -#define STREAMIO_H - -#include #include #include #include -#include #include #include #include +#include +#include + + #define MSG_META_CHANGE 'META' + class Station; class StreamIO; + typedef void (StreamIO::*DataFunc)(BUrlRequest* request, const char* data, off_t position, ssize_t size, int next); -class DataFuncs -{ + +class DataFuncs { public: - DataFuncs() : fSize(10), fLast(-1) + DataFuncs() + : + fSize(10), + fLast(-1) { for (int i = 0; i < fSize; i++) fFuncs[i] = NULL; }; - void Add(DataFunc f) { fFuncs[++fLast] = f; } - void Add(DataFunc f, int before) + + + void + Add(DataFunc f) + { + fFuncs[++fLast] = f; + } + + + void + Add(DataFunc f, int before) { for (int i = fLast; i >= before; i--) fFuncs[i + 1] = fFuncs[i]; fFuncs[before] = f; fLast++; }; - void Remove(int index) + + + void + Remove(int index) { - DataFunc result = fFuncs[index]; for (int i = index; i < fLast; i++) fFuncs[i] = fFuncs[i + 1]; fFuncs[fLast--] = NULL; }; - DataFunc Item(int index) { return fFuncs[index]; }; - DataFunc First() { return fFuncs[0]; }; + + + DataFunc Item(int index) { return fFuncs[index]; }; + DataFunc First() { return fFuncs[0]; }; private: - DataFunc fFuncs[10]; - int fSize; - int fLast; + DataFunc fFuncs[10]; + int fSize; + int fLast; }; -class StreamIO : public BAdapterIO, protected BUrlProtocolListener -{ + +class StreamIO : public BAdapterIO, protected BUrlProtocolListener { public: - StreamIO(Station* station, BLooper* MetaListener = NULL); - virtual ~StreamIO(); - virtual void GetFlags(int32* flags) const; - virtual ssize_t WriteAt(off_t position, const void* buffer, size_t size); - virtual ssize_t ReadAt(off_t position, void* buffer, size_t size); - virtual status_t SetSize(off_t size); - virtual status_t Open(); - virtual bool IsRunning() const; - void SetLimiter(size_t limit = 0); - size_t Buffered(); + StreamIO(Station* station, + BLooper* metaListener = NULL); + virtual ~StreamIO(); + + virtual void GetFlags(int32* flags) const; + + virtual ssize_t WriteAt(off_t position, const void* buffer, + size_t size); + virtual ssize_t ReadAt(off_t position, void* buffer, + size_t size); + + virtual status_t SetSize(off_t size); + + virtual status_t Open(); + virtual bool IsRunning() const; + + void SetLimiter(size_t limit = 0); + size_t Buffered(); protected: - virtual status_t SeekRequested(off_t position); - virtual void HeadersReceived( - BUrlRequest* request, const BUrlResult& result); - virtual void DataRedirectReceived(BUrlRequest* request, const char* data, - off_t position, ssize_t size, int next); - virtual void DataWithMetaReceived(BUrlRequest* request, const char* data, - off_t position, ssize_t size, int next); - virtual void DataUnsyncedReceived(BUrlRequest* request, const char* data, - off_t position, ssize_t size, int next); - virtual void DataSyncedReceived(BUrlRequest* request, const char* data, - off_t position, ssize_t size, int next); - virtual void DataReceived( - BUrlRequest* request, const char* data, off_t position, ssize_t size); - virtual void RequestCompleted(BUrlRequest* request, bool success); - // virtual void DebugMessage(BUrlRequest* caller, - // BUrlProtocolDebugMessage type, - // const char* text); - void UpdateSize(); - void ProcessMeta(); + virtual status_t SeekRequested(off_t position); + virtual void HeadersReceived( + BUrlRequest* request, + const BUrlResult& result); + virtual void DataRedirectReceived(BUrlRequest* request, + const char* data, off_t position, + ssize_t size, int next); + virtual void DataWithMetaReceived(BUrlRequest* request, + const char* data, off_t position, + ssize_t size, int next); + virtual void DataUnsyncedReceived(BUrlRequest* request, + const char* data, off_t position, + ssize_t size, int next); + virtual void DataSyncedReceived(BUrlRequest* request, + const char* data, off_t position, + ssize_t size, int next); + + virtual void DataReceived(BUrlRequest* request, + const char* data, off_t position, + ssize_t size); + virtual void RequestCompleted(BUrlRequest* request, + bool success); + virtual void DebugMessage(BUrlRequest* caller, + BUrlProtocolDebugMessage type, + const char* text); + + void UpdateSize(); + void ProcessMeta(); private: - DataFuncs fDataFuncs; - Station* fStation; - BInputAdapter* fInputAdapter; - BHttpRequest* fReq; - thread_id fReqThread; - off_t fPosition; - off_t fTotalSize; - bool fIsMutable; - size_t fMetaInt; - char fMetaBuffer[512]; - size_t fMetaSize; - off_t fUntilMetaStart; - off_t fUntilMetaEnd; - bigtime_t fReqStartTime; - BLooper* fMetaListener; - BString fStreamTitle; - enum FrameSync { none, first, done }; - FrameSync fFrameSync; - size_t fLimit; - size_t fBuffered; - const char* icyName; + enum FrameSync {none, first, done}; + + Station* fStation; + BHttpRequest* fReq; + thread_id fReqThread; + off_t fPosition; + off_t fTotalSize; + size_t fMetaInt; + size_t fMetaSize; + off_t fUntilMetaStart; + off_t fUntilMetaEnd; + BLooper* fMetaListener; + FrameSync fFrameSync; + size_t fLimit; + size_t fBuffered; + + DataFuncs fDataFuncs; + BInputAdapter* fInputAdapter; + BString fStreamTitle; + + const char* fIcyName; + bool fIsMutable; + char fMetaBuffer[512]; + bigtime_t fReqStartTime; + }; -#endif /* STREAMIO_H */ + +#endif // _STREAM_IO_H diff --git a/StreamPlayer.cpp b/StreamPlayer.cpp index c3a50dc..68ad66e 100644 --- a/StreamPlayer.cpp +++ b/StreamPlayer.cpp @@ -1,10 +1,21 @@ /* - * File: StreamPlayer.cpp - * Author: Kai Niessen + * Copyright (C) 2017 Kai Niessen * - * Created on January 25, 2016, 6:49 PM + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + #include #include #include @@ -14,27 +25,24 @@ #include "StreamIO.h" #include "StreamPlayer.h" -#define milliseconds *1000 -#define seconds *1000 milliseconds #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "StreamPlayer" -StreamPlayer::StreamPlayer(Station* station, BLooper* Notify) +StreamPlayer::StreamPlayer(Station* station, BLooper* notify) : - BLocker(), + BLocker("StreamPlayer"), fStation(station), - fNotify(Notify), - fNoNotifyCount(0), + fNotify(notify), fMediaFile(NULL), fPlayer(NULL), - fState(Stopped) + fState(StreamPlayer::Stopped) { TRACE("Trying to set player for stream %s\n", station->StreamUrl().UrlString().String()); - fStream = new (std::nothrow) StreamIO(station, Notify); + fStream = new(std::nothrow) StreamIO(station, notify); fInitStatus = fStream->Open(); if (fInitStatus != B_OK) { MSG("Error retrieving stream from %s - %s\n", @@ -46,133 +54,215 @@ StreamPlayer::StreamPlayer(Station* station, BLooper* Notify) StreamPlayer::~StreamPlayer() { - setState(Stopped); - if (fInitStatus == B_OK && fPlayer) + _SetState(StreamPlayer::Stopped); + if (fInitStatus == B_OK && fPlayer != NULL) fPlayer->Stop(true, false); + delete fPlayer; delete fStream; delete fMediaFile; } -void -StreamPlayer::GetDecodedChunk(void* cookie, void* buffer, size_t size, - const media_raw_audio_format& format) -{ - StreamPlayer* player = (StreamPlayer*) (cookie); - BMediaFile* fMediaFile = player->fMediaFile; - int64 reqFrames = size / format.channel_count - / (format.format & media_raw_audio_format::B_AUDIO_SIZE_MASK); - fMediaFile->TrackAt(0)->ReadFrames( - buffer, &reqFrames, &player->fHeader, &player->fInfo); -} - - status_t StreamPlayer::Play() { switch (fState) { - case Playing: - case Buffering: + case StreamPlayer::Playing: + case StreamPlayer::Buffering: + case StreamPlayer::InActive: break; - case Stopped: + + case StreamPlayer::Stopped: + { thread_id playThreadId - = spawn_thread((thread_func) StreamPlayer::StartPlayThreadFunc, + = spawn_thread((thread_func)StreamPlayer::_StartPlayThreadFunc, fStation->Name()->String(), B_NORMAL_PRIORITY, this); resume_thread(playThreadId); + break; + } } + return B_OK; } -#ifdef DEBUG + +status_t +StreamPlayer::Stop() +{ + switch (fState) { + case StreamPlayer::Buffering: + fStopRequested = true; + break; + + case StreamPlayer::Playing: + { + Lock(); + + if (fPlayer) { + fPlayer->Stop(false, true); + delete fPlayer; + fPlayer = NULL; + } + + if (fMediaFile) { + fMediaFile->CloseFile(); + delete fMediaFile; + fMediaFile = NULL; + } + + Unlock(); + _SetState(StreamPlayer::Stopped); + + break; + } + + case StreamPlayer::Stopped: + case StreamPlayer::InActive: + break; + } + return B_OK; +} + + +float +StreamPlayer::Volume() +{ + if (fPlayer != NULL) { + media_node node; + int32 paramID; + float minDB, maxDB; + fPlayer->GetVolumeInfo(&node, ¶mID, &minDB, &maxDB); + + return (fPlayer->VolumeDB(false) - minDB) / (maxDB - minDB); + } else + return 0; +} void -dumpMessage(BMessage* msg) +StreamPlayer::SetVolume(float volume) { - int n = msg->CountNames(B_ANY_TYPE); - type_code typeCode; - int32 arraySize; - char* name; - for (long int i = 0; i < n; i++) { - msg->GetInfo( - (long unsigned int) B_ANY_TYPE, i, &name, &typeCode, &arraySize); - MSG("Meta data %s = %s\n", name, name); + if (fPlayer != NULL) { + media_node node; + int32 paramID; + float minDB, maxDB; + fPlayer->GetVolumeInfo(&node, ¶mID, &minDB, &maxDB); + fPlayer->SetVolumeDB(minDB + volume * (maxDB - minDB)); } } -#else -void inline dumpMessage(BMessage* msg) +void +StreamPlayer::_SetState(StreamPlayer::PlayState state) { + if (fState != state) + fState = state; + + if (fNotify != NULL) { + BMessage* notification = new BMessage(MSG_PLAYER_STATE_CHANGED); + BMessenger messenger(fNotify); + notification->AddPointer("player", this); + notification->AddInt32("state", fState); + messenger.SendMessage(notification, fNotify); + } +} + + +void +StreamPlayer::_GetDecodedChunk(void* cookie, void* buffer, size_t size, + const media_raw_audio_format& format) +{ + StreamPlayer* player = (StreamPlayer*)cookie; + BMediaFile* fMediaFile = player->fMediaFile; + + int64 reqFrames = size / format.channel_count + / (format.format & media_raw_audio_format::B_AUDIO_SIZE_MASK); + fMediaFile->TrackAt(0)->ReadFrames( + buffer, &reqFrames, &player->fHeader, &player->fInfo); } -#endif status_t -StreamPlayer::StartPlayThreadFunc(StreamPlayer* _this) +StreamPlayer::_StartPlayThreadFunc(StreamPlayer* _this) { _this->Lock(); - _this->setState(Buffering); + + _this->_SetState(StreamPlayer::Buffering); _this->fStopRequested = false; _this->fStream->SetLimiter(0x40000); - _this->fMediaFile = new (std::nothrow) BMediaFile(_this->fStream); + _this->fMediaFile = new(std::nothrow) BMediaFile(_this->fStream); + _this->fInitStatus = _this->fMediaFile->InitCheck(); - if (_this->fInitStatus != B_OK) + if (_this->fInitStatus != B_OK) { MSG("Error creating media extractor for %s - %s\n", _this->fStation->StreamUrl().UrlString().String(), strerror(_this->fInitStatus)); + } if (_this->fInitStatus != B_OK || _this->fStopRequested) { delete _this->fStream; _this->fStream = NULL; delete _this->fMediaFile; _this->fMediaFile = NULL; + _this->Unlock(); - _this->setState(Stopped); + _this->_SetState(StreamPlayer::Stopped); + return _this->fInitStatus; } + _this->fStream->SetLimiter(0); - if (_this->fMediaFile->CountTracks() == 0) + + if (_this->fMediaFile->CountTracks() == 0) { MSG("No track found inside stream %s - %s\n", _this->fStation->StreamUrl().UrlString().String(), strerror(_this->fInitStatus)); + } - if (_this->fInitStatus != B_OK || _this->fStopRequested) { + if (_this->fStopRequested) { delete _this->fMediaFile; _this->fMediaFile = NULL; + _this->Unlock(); - _this->setState(Stopped); + _this->_SetState(StreamPlayer::Stopped); + return _this->fInitStatus; } _this->fInitStatus = _this->fMediaFile->TrackAt(0)->GetCodecInfo(&_this->fCodecInfo); - if (_this->fInitStatus != B_OK) + if (_this->fInitStatus != B_OK) { MSG("Could not create decoder for %s - %s\n", _this->fStation->StreamUrl().UrlString().String(), strerror(_this->fInitStatus)); + } if (_this->fInitStatus != B_OK || _this->fStopRequested) { delete _this->fMediaFile; _this->fMediaFile = NULL; + _this->Unlock(); - _this->setState(Stopped); + _this->_SetState(StreamPlayer::Stopped); + return _this->fInitStatus; } _this->fInitStatus = _this->fMediaFile->TrackAt(0)->DecodedFormat(&_this->fDecodedFormat); - if (_this->fInitStatus != B_OK) + if (_this->fInitStatus != B_OK) { MSG("Could not negotiate output format - %s\n", strerror(_this->fInitStatus)); + } if (_this->fInitStatus != B_OK || _this->fStopRequested) { delete _this->fMediaFile; _this->fMediaFile = NULL; + _this->Unlock(); - _this->setState(Stopped); + _this->_SetState(StreamPlayer::Stopped); + return _this->fInitStatus; } @@ -181,110 +271,46 @@ StreamPlayer::StartPlayThreadFunc(StreamPlayer* _this) _this->fDecodedFormat.u.raw_audio.frame_rate); _this->fPlayer = new BSoundPlayer(&_this->fDecodedFormat.u.raw_audio, - _this->fStation->Name()->String(), &StreamPlayer::GetDecodedChunk, NULL, - _this); + _this->fStation->Name()->String(), &StreamPlayer::_GetDecodedChunk, + NULL, _this); - _this->fStatus = _this->fPlayer->InitCheck(); - if (_this->fStatus != B_OK) + _this->fInitStatus = _this->fPlayer->InitCheck(); + if (_this->fInitStatus != B_OK) { MSG("Sound Player failed to initialize (%s)\r\n", - strerror(_this->fStatus)); + strerror(_this->fInitStatus)); + } if (_this->fInitStatus != B_OK || _this->fStopRequested) { _this->fPlayer->Stop(true, true); delete _this->fPlayer; _this->fPlayer = NULL; + _this->Unlock(); - _this->setState(Stopped); - return _this->fStatus; + _this->_SetState(StreamPlayer::Stopped); + + return _this->fInitStatus; } _this->fPlayer->Preroll(); _this->fInitStatus = _this->fPlayer->Start(); - if (_this->fInitStatus != B_OK) - MSG("Sound Player failed to start (%s)\r\n", strerror(_this->fStatus)); + if (_this->fInitStatus != B_OK) { + MSG("Sound Player failed to start (%s)\r\n", + strerror(_this->fInitStatus)); + } if (_this->fInitStatus != B_OK || _this->fStopRequested) { delete _this->fPlayer; _this->fPlayer = NULL; + _this->Unlock(); - _this->setState(Stopped); + _this->_SetState(StreamPlayer::Stopped); + return _this->fInitStatus; } - _this->setState(Playing); + _this->_SetState(StreamPlayer::Playing); _this->Unlock(); - return _this->fStatus; -} - - -status_t -StreamPlayer::Stop() -{ - switch (fState) { - case Buffering: - fStopRequested = true; - break; - case Playing: - Lock(); - if (fPlayer) { - fPlayer->Stop(false, true); - delete fPlayer; - fPlayer = NULL; - } - if (fMediaFile) { - fMediaFile->CloseFile(); - delete fMediaFile; - fMediaFile = NULL; - } - Unlock(); - setState(StreamPlayer::Stopped); - break; - case Stopped: - case StreamPlayer::InActive: - break; - } - return B_OK; -} - - -void -StreamPlayer::setState(StreamPlayer::PlayState state) -{ - if (fState != state) - fState = state; - if (fNotify) { - BMessage* notification = new BMessage(MSG_PLAYER_STATE_CHANGED); - BMessenger messenger(fNotify); - notification->AddPointer("player", this); - notification->AddInt32("state", fState); - messenger.SendMessage(notification, fNotify); - } -} - -float -StreamPlayer::Volume() -{ - if (fPlayer) { - media_node node; - int32 paramID; - float minDB, maxDB; - fPlayer->GetVolumeInfo(&node, ¶mID, &minDB, &maxDB); - return (fPlayer->VolumeDB(false) - minDB) / (maxDB - minDB); - } else - return 0; -} - - -void -StreamPlayer::SetVolume(float volume) -{ - if (fPlayer) { - media_node node; - int32 paramID; - float minDB, maxDB; - fPlayer->GetVolumeInfo(&node, ¶mID, &minDB, &maxDB); - fPlayer->SetVolumeDB(minDB + volume * (maxDB - minDB)); - } + return _this->fInitStatus; } diff --git a/StreamPlayer.h b/StreamPlayer.h index 8abd3c9..f6db508 100644 --- a/StreamPlayer.h +++ b/StreamPlayer.h @@ -1,21 +1,31 @@ /* - * File: HttpPlayer.h - * Author: Kai Niessen + * Copyright (C) 2017 Kai Niessen * - * Created on January 25, 2016, 6:49 PM + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ +#ifndef _STREAM_PLAYER_H +#define _STREAM_PLAYER_H + -#ifndef STREAMPLAYER_H -#define STREAMPLAYER_H -//#include -//#include +#include +#include +#include #include "Station.h" #include "StreamIO.h" -#include -#include -#include + // Notification Messages #define MSG_PLAYER_STATE_CHANGED \ @@ -23,41 +33,50 @@ #define MSG_PLAYER_BUFFER_LEVEL \ 'mPBL' // "player" = StreamPlayer*, "level" = float percent buffer filled + class Station; -class StreamPlayer : BLocker -{ + +class StreamPlayer : private BLocker { public: - StreamPlayer(Station* station, BLooper* Notify = NULL); - virtual ~StreamPlayer(); - inline status_t InitCheck() { return this->fInitStatus; } - inline Station* GetStation() { return this->fStation; } - status_t Play(); - status_t Stop(); - float Volume(); - void SetVolume(float volume); - enum PlayState { InActive = -1, Stopped, Playing, Buffering }; - inline PlayState State() { return fState; } + StreamPlayer(Station* station, + BLooper* notify = NULL); + virtual ~StreamPlayer(); + + inline status_t InitCheck() { return fInitStatus; } + inline Station* GetStation() { return fStation; } + status_t Play(); + status_t Stop(); + float Volume(); + void SetVolume(float volume); + + enum PlayState { InActive = -1, Stopped, Playing, Buffering }; + inline PlayState State() { return fState; } + +private: + void _SetState(PlayState state); + + static status_t _StartPlayThreadFunc(StreamPlayer* _this); + static void _GetDecodedChunk(void* cookie, void* buffer, + size_t size, + const media_raw_audio_format& format); private: - PlayState fState; - void setState(PlayState state); - status_t fStatus, fInitStatus; - BLooper* fNotify; - int fNoNotifyCount; - Station* fStation; - StreamIO* fStream; - BMediaFile* fMediaFile; - BSoundPlayer* fPlayer; - media_codec_info fCodecInfo; - media_format fDecodedFormat; - media_header fHeader; - media_decode_info fInfo; - BLocker fLock; - bool fStopRequested; - static status_t StartPlayThreadFunc(StreamPlayer* _this); - static void GetDecodedChunk(void* cookie, void* buffer, size_t size, - const media_raw_audio_format& format); + Station* fStation; + BLooper* fNotify; + BMediaFile* fMediaFile; + StreamIO* fStream; + BSoundPlayer* fPlayer; + PlayState fState; + + status_t fInitStatus; + bool fStopRequested; + + media_codec_info fCodecInfo; + media_format fDecodedFormat; + media_header fHeader; + media_decode_info fInfo; }; -#endif /* STREAMPLAYER_H */ + +#endif // _STREAM_PLAYER_H diff --git a/StreamRadio-version.rdef b/StreamRadio-version.rdef deleted file mode 100644 index 6158981..0000000 --- a/StreamRadio-version.rdef +++ /dev/null @@ -1,7 +0,0 @@ -resource app_signature "application/x-vnd.Fishpond-Radio"; -resource app_version { - major = 0, middle = 0, minor = 4, - variety = B_APPV_DEVELOPMENT, internal = 1, - short_info="A player for online radio", - long_info="Find and stream online radio stations" -}; diff --git a/StreamRadio.rdef b/StreamRadio.rdef index edd454d..8d0fb46 100644 --- a/StreamRadio.rdef +++ b/StreamRadio.rdef @@ -1,5 +1,5 @@ -resource app_signature "application/x-vnd.Fishpond-Radio"; +resource app_signature "application/x-vnd.Fishpond-StreamRadio"; resource file_types message { "types" = "audio/x-mpegurl", diff --git a/Utils.cpp b/Utils.cpp index 5a0c891..4664f5a 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -12,17 +12,12 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ -/* - * File: Utils.cpp - * Author: Kai Niessen - * - * Created on April 16, 2017, 1:15 PM - */ #include "Utils.h" + #include #include #include @@ -33,11 +28,12 @@ Utils::ResourceBitmap(int32 id) { size_t size; const char* data - = (const char*) be_app->AppResources()->FindResource('BBMP', id, &size); - if (size && data) { + = (const char*)be_app->AppResources()->FindResource('BBMP', id, &size); + if (size != 0 && data != NULL) { BMessage msg; - status_t status = msg.Unflatten(data); - return (BBitmap*) BBitmap::Instantiate(&msg); + if (msg.Unflatten(data) == B_OK) + return (BBitmap*)BBitmap::Instantiate(&msg); } + return NULL; } diff --git a/Utils.h b/Utils.h index 3f428c9..a4a6512 100644 --- a/Utils.h +++ b/Utils.h @@ -12,34 +12,30 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . */ +#ifndef _UTILS_H +#define _UTILS_H -/* - * File: Utils.h - * Author: Kai Niessen - * - * Created on April 16, 2017, 1:15 PM - */ - -#ifndef UTILS_H -#define UTILS_H #include #include + #define RES_BANNER 100 #define RES_BN_SEARCH 10 #define RES_BN_STOPPED 1 #define RES_BN_SEARCH 10 #define RES_BN_WEB 11 -class Utils -{ + +class Utils { public: - Utils(){}; - virtual ~Utils(){}; - static BBitmap* ResourceBitmap(int32 id); + Utils() {}; + virtual ~Utils() {}; + + static BBitmap* ResourceBitmap(int32 id); }; -#endif /* UTILS_H */ + +#endif // _UTILS_H diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml deleted file mode 100644 index 01cfb81..0000000 --- a/nbproject/configurations.xml +++ /dev/null @@ -1,427 +0,0 @@ - - - - - About.cpp - About.h - Buffering.h - Debug.h - Expander.cpp - Expander.h - HttpUtils.cpp - HttpUtils.h - MainWindow.cpp - MainWindow.h - RadioApp.cpp - RadioApp.h - RadioSettings.cpp - RadioSettings.h - Station.cpp - Station.h - StationFinder.cpp - StationFinder.h - StationFinderListenLive.cpp - StationFinderListenLive.h - StationFinderRadioNetwork.cpp - StationFinderRadioNetwork.h - StationListView.cpp - StationListView.h - StationPanel.cpp - StationPanel.h - StreamIO.cpp - StreamIO.h - StreamPlayer.cpp - StreamPlayer.h - Utils.cpp - Utils.h - - - /boot/home/projects/Haiku-Radio/Makefile - /boot/home/projects/Radio/nbproject/private/launcher.properties - - - ^(nbproject)$ - - /boot/home/projects/Haiku-Radio - - /boot/home/projects/Radio/Makefile - - - - default - false - false - - - - - - /boot/home/projects/Haiku-Radio - ${MAKE} -f Makefile - ${MAKE} -f Makefile clean - dist/Radio - - - /boot/system/develop/headers/private/media/experimental - /boot/system/develop/headers/private/shared - - - - - . - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - default - false - false - - - - - - /boot/home/projects/Haiku-Radio - ${MAKE} -f Makefile "DEBUGGER=TRUE" - ${MAKE} -f Makefile clean - dist/Radio - - - /boot/system/develop/headers/private/media/experimental - /boot/system/develop/headers/private/shared - - - - - . - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/nbproject/private/CodeAssistancePathMapper.properties b/nbproject/private/CodeAssistancePathMapper.properties deleted file mode 100644 index 7f5ded8..0000000 --- a/nbproject/private/CodeAssistancePathMapper.properties +++ /dev/null @@ -1 +0,0 @@ -# Automatic path mapper. CRC = 1 diff --git a/nbproject/private/Default-build.log b/nbproject/private/Default-build.log deleted file mode 100644 index 894a1b1..0000000 --- a/nbproject/private/Default-build.log +++ /dev/null @@ -1,56 +0,0 @@ -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/Utils.d" Utils.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/StreamPlayer.d" StreamPlayer.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/StreamIO.d" StreamIO.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/StationPanel.d" StationPanel.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/StationListView.d" StationListView.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/StationFinderRadioNetwork.d" StationFinderRadioNetwork.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/StationFinderListenLive.d" StationFinderListenLive.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/StationFinder.d" StationFinder.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/Station.d" Station.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/RadioSettings.d" RadioSettings.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/RadioApp.d" RadioApp.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/MainWindow.d" MainWindow.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/HttpUtils.d" HttpUtils.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/Expander.d" Expander.cpp -mkdir -p objects.x86-cc2-release; \ -mkdepend -I./ -Iobjects.x86-cc2-release/ -p .cpp:objects.x86-cc2-release/%n.o -m -f "objects.x86-cc2-release/About.d" About.cpp -g++ -c About.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/About.o" -g++ -c Expander.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/Expander.o" -g++ -c HttpUtils.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/HttpUtils.o" -g++ -c MainWindow.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/MainWindow.o" -g++ -c RadioApp.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/RadioApp.o" -g++ -c RadioSettings.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/RadioSettings.o" -g++ -c Station.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/Station.o" -g++ -c StationFinder.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/StationFinder.o" -g++ -c StationFinderListenLive.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/StationFinderListenLive.o" -g++ -c StationFinderRadioNetwork.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/StationFinderRadioNetwork.o" -g++ -c StationListView.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/StationListView.o" -g++ -c StationPanel.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/StationPanel.o" -g++ -c StreamIO.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/StreamIO.o" -g++ -c StreamPlayer.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/StreamPlayer.o" -g++ -c Utils.cpp -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU -o "objects.x86-cc2-release/Utils.o" -echo "resource app_signature \"application/x-vnd.Fishpond-Radio\";" > Radio-version.rdef -echo "resource app_version {" >> Radio-version.rdef -echo " major = 0, middle = 0, minor = 3," >> Radio-version.rdef -echo " variety = B_APPV_ALPHA, internal = 9," >> Radio-version.rdef -echo " short_info=\"Radio\"," >> Radio-version.rdef -echo " long_info=\"Radio v0.0.3-9, ©Fishpond 2017\"" >> Radio-version.rdef -echo "};" >> Radio-version.rdef -cat Radio-version.rdef | cc -E -I./ -Iobjects.x86-cc2-release/ -I- -I/boot/system/develop/headers/private/media -I/boot/system/develop/headers/private/media/experimental -I/boot/system/develop/headers/private/shared -I/boot/system/develop/headers/libxml2 -O3 -DHAIKU_TARGET_PLATFORM_HAIKU - | grep -av '^#' | rc -I ./ -o "objects.x86-cc2-release/Radio-version.rsrc" - -cc -o "dist/Radio" objects.x86-cc2-release/About.o objects.x86-cc2-release/Expander.o objects.x86-cc2-release/HttpUtils.o objects.x86-cc2-release/MainWindow.o objects.x86-cc2-release/RadioApp.o objects.x86-cc2-release/RadioSettings.o objects.x86-cc2-release/Station.o objects.x86-cc2-release/StationFinder.o objects.x86-cc2-release/StationFinderListenLive.o objects.x86-cc2-release/StationFinderRadioNetwork.o objects.x86-cc2-release/StationListView.o objects.x86-cc2-release/StationPanel.o objects.x86-cc2-release/StreamIO.o objects.x86-cc2-release/StreamPlayer.o objects.x86-cc2-release/Utils.o -Xlinker -soname=_APP_ -L./ -Lobjects.x86-cc2-release/ -lstdc++.r4 -lbe -ltranslation -lbnetapi -lmedia -lxml2 -xres -o ./dist/Radio Radio.rsrc objects.x86-cc2-release/Radio-version.rsrc -mimeset -f "dist/Radio" diff --git a/nbproject/private/Release.properties b/nbproject/private/Release.properties deleted file mode 100644 index e69de29..0000000 diff --git a/nbproject/private/configurations.xml b/nbproject/private/configurations.xml deleted file mode 100644 index 4489b4d..0000000 --- a/nbproject/private/configurations.xml +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - - - - - - About.cpp - About.h - Buffering.h - Debug.h - Expander.cpp - Expander.h - HttpUtils.cpp - HttpUtils.h - MainWindow.cpp - MainWindow.h - RadioApp.cpp - RadioApp.h - RadioSettings.cpp - RadioSettings.h - Station.cpp - Station.h - StationFinder.cpp - StationFinder.h - StationFinderListenLive.cpp - StationFinderListenLive.h - StationFinderRadioNetwork.cpp - StationFinderRadioNetwork.h - StationListView.cpp - StationListView.h - StationPanel.cpp - StationPanel.h - StreamIO.cpp - StreamIO.h - StreamPlayer.cpp - StreamPlayer.h - Utils.cpp - Utils.h - - - /boot/home/projects/Radio/Makefile - - - - localhost - 5 - - - - . - ${AUTO_FOLDER} - - ${AUTO_FOLDER} - - ${MAKE} ${ITEM_NAME}.o - ${AUTO_COMPILE} - - ${AUTO_COMPILE} - - - - - - - - - - - - - - - gdb - - - - "${OUTPUT_PATH}" - dist/Radio - Radio - - Radio - dist - false - 0 - 0 - - - - - - - localhost - 5 - - - - . - ${AUTO_FOLDER} - - ${AUTO_FOLDER} - - ${MAKE} ${ITEM_NAME}.o - ${AUTO_COMPILE} - - ${AUTO_COMPILE} - - - - - - - - - - - - - - - gdb - - - - "${OUTPUT_PATH}" - dist/Radio - Radio - - Radio - dist - false - 0 - 0 - - - - - - diff --git a/nbproject/private/launcher.properties b/nbproject/private/launcher.properties deleted file mode 100644 index 6cc2127..0000000 --- a/nbproject/private/launcher.properties +++ /dev/null @@ -1,40 +0,0 @@ -# Launchers File syntax: -# -# [Must-have property line] -# launcher1.runCommand= -# [Optional extra properties] -# launcher1.displayName= -# launcher1.buildCommand= -# launcher1.runDir= -# launcher1.symbolFiles= -# launcher1.env.= -# (If this value is quoted with ` it is handled as a native command which execution result will become the value) -# [Common launcher properties] -# common.runDir= -# (This value is overwritten by a launcher specific runDir value if the latter exists) -# common.env.= -# (Environment variables from common launcher are merged with launcher specific variables) -# common.symbolFiles= -# (This value is overwritten by a launcher specific symbolFiles value if the latter exists) -# -# In runDir, symbolFiles and env fields you can use these macroses: -# ${PROJECT_DIR} - project directory absolute path -# ${OUTPUT_PATH} - linker output path (relative to project directory path) -# ${OUTPUT_BASENAME}- linker output filename -# ${TESTDIR} - test files directory (relative to project directory path) -# ${OBJECTDIR} - object files directory (relative to project directory path) -# ${CND_DISTDIR} - distribution directory (relative to project directory path) -# ${CND_BUILDDIR} - build directory (relative to project directory path) -# ${CND_PLATFORM} - platform name -# ${CND_CONF} - configuration name -# ${CND_DLIB_EXT} - dynamic library extension -# -# All the project launchers must be listed in the file! -# -# launcher1.runCommand=... -# launcher2.runCommand=... -# ... -# common.runDir=... -# common.env.KEY=VALUE - -# launcher1.runCommand= \ No newline at end of file diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml deleted file mode 100644 index 5caf8ac..0000000 --- a/nbproject/private/private.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - true - - - 0 - 1 - - - - - file:/boot/home/projects/Haiku-Radio/Makefile - file:/boot/home/projects/Haiku-Radio/Station.cpp - file:/boot/home/projects/Haiku-Radio/StationFinder.cpp - file:/boot/home/projects/Haiku-Radio/PackageInfo - - - diff --git a/nbproject/project.xml b/nbproject/project.xml deleted file mode 100644 index 2302750..0000000 --- a/nbproject/project.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - org.netbeans.modules.cnd.makeproject - - - Radio - - cpp - h - UTF-8 - - - /boot/home/projects/Haiku-Radio - - - - Release - 0 - - - Debug - 0 - - - - false - - - -