]> git.basschouten.com Git - openhab-addons.git/blob
a522329380358842d69e93a84f545889d3664b4e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.squeezebox.internal;
14
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.util.Locale;
18 import java.util.Set;
19
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.squeezebox.internal.handler.SqueezeBoxPlayerHandler;
22 import org.openhab.core.audio.AudioFormat;
23 import org.openhab.core.audio.AudioHTTPServer;
24 import org.openhab.core.audio.AudioSink;
25 import org.openhab.core.audio.AudioSinkSync;
26 import org.openhab.core.audio.AudioStream;
27 import org.openhab.core.audio.FileAudioStream;
28 import org.openhab.core.audio.StreamServed;
29 import org.openhab.core.audio.URLAudioStream;
30 import org.openhab.core.audio.UnsupportedAudioFormatException;
31 import org.openhab.core.audio.UnsupportedAudioStreamException;
32 import org.openhab.core.audio.utils.AudioStreamUtils;
33 import org.openhab.core.library.types.PercentType;
34 import org.openhab.core.library.types.StringType;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * This makes a SqueezeBox Player serve as an {@link AudioSink}-
40  *
41  * @author Mark Hilbush - Initial contribution
42  * @author Mark Hilbush - Add callbackUrl
43  */
44 public class SqueezeBoxAudioSink extends AudioSinkSync {
45     private final Logger logger = LoggerFactory.getLogger(SqueezeBoxAudioSink.class);
46
47     private static final Set<AudioFormat> SUPPORTED_FORMATS = Set.of(AudioFormat.WAV, AudioFormat.MP3);
48     private static final Set<Class<? extends AudioStream>> SUPPORTED_STREAMS = Set.of(AudioStream.class);
49
50     // Needed because Squeezebox does multiple requests for the stream
51     private static final int STREAM_TIMEOUT = 10;
52
53     private String callbackUrl;
54
55     private AudioHTTPServer audioHTTPServer;
56     private SqueezeBoxPlayerHandler playerHandler;
57
58     public SqueezeBoxAudioSink(SqueezeBoxPlayerHandler playerHandler, AudioHTTPServer audioHTTPServer,
59             String callbackUrl) {
60         this.playerHandler = playerHandler;
61         this.audioHTTPServer = audioHTTPServer;
62         this.callbackUrl = callbackUrl;
63         if (callbackUrl != null && !callbackUrl.isEmpty()) {
64             logger.debug("SqueezeBox AudioSink created with callback URL: {}", callbackUrl);
65         }
66     }
67
68     @Override
69     public String getId() {
70         return playerHandler.getThing().getUID().toString();
71     }
72
73     @Override
74     public String getLabel(Locale locale) {
75         return playerHandler.getThing().getLabel();
76     }
77
78     @Override
79     public void processSynchronously(AudioStream audioStream)
80             throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
81         if (audioStream == null) {
82             return;
83         }
84         AudioFormat format = audioStream.getFormat();
85         if (!AudioFormat.WAV.isCompatible(format) && !AudioFormat.MP3.isCompatible(format)) {
86             tryClose(audioStream);
87             throw new UnsupportedAudioFormatException("Currently only MP3 and WAV formats are supported: ", format);
88         }
89
90         String url;
91         if (audioStream instanceof URLAudioStream urlAudioStream) {
92             url = urlAudioStream.getURL();
93             tryClose(audioStream);
94         } else {
95             try {
96                 // Since Squeezebox will make multiple requests for the stream, set multiple to true
97                 StreamServed streamServed = audioHTTPServer.serve(audioStream, STREAM_TIMEOUT, true);
98                 url = streamServed.url();
99
100                 if (AudioFormat.WAV.isCompatible(format)) {
101                     url += AudioStreamUtils.EXTENSION_SEPARATOR + FileAudioStream.WAV_EXTENSION;
102                 } else if (AudioFormat.MP3.isCompatible(format)) {
103                     url += AudioStreamUtils.EXTENSION_SEPARATOR + FileAudioStream.MP3_EXTENSION;
104                 }
105
106                 // Form the URL for streaming the notification from the OH web server
107                 // Use the callback URL if it is set in the binding configuration
108                 String host = callbackUrl == null || callbackUrl.isEmpty() ? playerHandler.getHostAndPort()
109                         : callbackUrl;
110                 if (host == null) {
111                     logger.warn("Unable to get host/port from which to stream notification");
112                     tryClose(audioStream);
113                     return;
114                 }
115                 url = host + url;
116             } catch (IOException e) {
117                 tryClose(audioStream);
118                 throw new UnsupportedAudioStreamException(
119                         "Squeezebox binding was not able to handle the audio stream (cache on disk failed)",
120                         audioStream.getClass(), e);
121             }
122         }
123
124         logger.debug("Processing audioStream {} of format {}", url, format);
125         playerHandler.playNotificationSoundURI(new StringType(url));
126     }
127
128     private void tryClose(@Nullable InputStream is) {
129         if (is != null) {
130             try {
131                 is.close();
132             } catch (IOException ignored) {
133             }
134         }
135     }
136
137     @Override
138     public Set<AudioFormat> getSupportedFormats() {
139         return SUPPORTED_FORMATS;
140     }
141
142     @Override
143     public Set<Class<? extends AudioStream>> getSupportedStreams() {
144         return SUPPORTED_STREAMS;
145     }
146
147     @Override
148     public PercentType getVolume() {
149         return playerHandler.getNotificationSoundVolume();
150     }
151
152     @Override
153     public void setVolume(PercentType volume) {
154         playerHandler.setNotificationSoundVolume(volume);
155     }
156 }