]> git.basschouten.com Git - openhab-addons.git/blob
b3588d33b0543d6bbbe622534ce1c0b0a340225c
[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.onkyo.internal;
14
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.net.MalformedURLException;
18 import java.net.URL;
19 import java.net.URLConnection;
20 import java.util.Arrays;
21
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.openhab.core.util.HexUtils;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * Class to handle Onkyo Album Arts.
29  *
30  * @author Pauli Anttila - Initial contribution
31  * @author Stewart Cossey - fixed bug in getAlbumArt function
32  */
33 public class OnkyoAlbumArt {
34
35     private final Logger logger = LoggerFactory.getLogger(OnkyoAlbumArt.class);
36
37     private enum State {
38         INVALID,
39         NOTSTARTED,
40         STARTED,
41         NEXT,
42         READY
43     }
44
45     private enum ImageType {
46         BMP,
47         JPEG,
48         URL,
49         NONE,
50         UNKNOWN
51     }
52
53     private StringBuilder albumArtStringBuilder = new StringBuilder();
54     private ImageType imageType = ImageType.UNKNOWN;
55     private State state = State.NOTSTARTED;
56     private String coverArtUrl;
57
58     public boolean isAlbumCoverTransferStarted() {
59         return state == State.STARTED;
60     }
61
62     public boolean isAlbumCoverReady() {
63         return state == State.READY;
64     }
65
66     public void clearAlbumArt() {
67         albumArtStringBuilder.setLength(0);
68         imageType = ImageType.UNKNOWN;
69         state = State.NOTSTARTED;
70         coverArtUrl = null;
71     }
72
73     public void addFrame(String data) {
74         if (data.length() <= 2) {
75             return;
76         }
77
78         char imgType = data.charAt(0);
79         imageType = getImageType(imgType);
80
81         char packetFlag = data.charAt(1);
82         String packetFlagStr = "unknown";
83
84         switch (packetFlag) {
85             case '0':
86                 if (state == State.NOTSTARTED || state == State.INVALID) {
87                     state = State.STARTED;
88                 } else {
89                     state = State.INVALID;
90                 }
91                 packetFlagStr = "Start";
92                 albumArtStringBuilder.setLength(0);
93                 break;
94             case '1':
95                 packetFlagStr = "Next";
96                 if (state == State.STARTED || state == State.NEXT) {
97                     state = State.NEXT;
98                 } else {
99                     state = State.INVALID;
100                 }
101                 break;
102             case '2':
103                 packetFlagStr = "End";
104                 if (state == State.STARTED || state == State.NEXT) {
105                     state = State.READY;
106                 } else {
107                     state = State.INVALID;
108                 }
109                 break;
110             case '-':
111                 packetFlagStr = "notUsed";
112                 state = State.READY;
113                 break;
114             default:
115                 state = State.INVALID;
116                 logger.debug("Unknown album art packet flag '{}'", packetFlag);
117         }
118
119         if (state != State.INVALID) {
120             switch (imageType) {
121                 case BMP:
122                 case JPEG:
123                     String picData = data.substring(2, data.length());
124                     logger.debug("Received album art fragment in '{}' format, packet flag '{}', picData '{}'",
125                             imageType, packetFlagStr, picData);
126                     albumArtStringBuilder.append(picData);
127                     break;
128                 case URL:
129                     coverArtUrl = data.substring(2);
130                     logger.debug("Received album art url '{}'", coverArtUrl);
131                     break;
132                 case NONE:
133                     logger.debug("Received information: album art not available");
134                     break;
135                 default:
136             }
137
138         } else {
139             logger.debug("Received album art fragment in wrong order, format '{}', packet flag '{}'", imageType,
140                     packetFlagStr);
141         }
142     }
143
144     public byte[] getAlbumArt() throws IllegalArgumentException {
145         byte[] data = null;
146
147         if (state == State.READY) {
148             switch (imageType) {
149                 case BMP:
150                 case JPEG:
151                     data = HexUtils.hexToBytes(albumArtStringBuilder.toString());
152                     break;
153                 case URL:
154                     data = downloadAlbumArt(coverArtUrl);
155                     // Workaround firmware bug providing incorrect headers causing them to be seen as body instead.
156                     if (data != null) {
157                         int bodyLength = data.length;
158                         int i = new String(data).indexOf("image/");
159                         if (i > 0) {
160                             while (i < bodyLength && (data[i] != '\r' && data[i] != '\n')) {
161                                 i++;
162                             }
163                             while (i < bodyLength && (data[i] == '\r' || data[i] == '\n')) {
164                                 i++;
165                             }
166                             data = Arrays.copyOfRange(data, i, bodyLength);
167                             logger.trace("Onkyo fixed picture data @ {}: {} ", i, new String(data));
168                         }
169                     }
170                     break;
171                 case NONE:
172                 default:
173             }
174             return data;
175         }
176
177         throw new IllegalArgumentException("Illegal Album Art");
178     }
179
180     private byte[] downloadAlbumArt(String albumArtUrl) {
181         try {
182             URL url = new URL(albumArtUrl);
183             URLConnection connection = url.openConnection();
184             try (InputStream inputStream = connection.getInputStream()) {
185                 return inputStream.readAllBytes();
186             }
187         } catch (MalformedURLException e) {
188             logger.warn("Album Art download failed from url '{}', reason {}", albumArtUrl, e.getMessage());
189         } catch (IOException e) {
190             logger.warn("Album Art download failed from url '{}', reason {}", albumArtUrl, e.getMessage());
191         }
192
193         return null;
194     }
195
196     private ImageType getImageType(char imgType) {
197         ImageType it = ImageType.UNKNOWN;
198         switch (imgType) {
199             case '0':
200                 it = ImageType.BMP;
201                 break;
202             case '1':
203                 it = ImageType.JPEG;
204                 break;
205             case '2':
206                 it = ImageType.URL;
207                 break;
208             case 'n':
209                 it = ImageType.NONE;
210                 break;
211             default:
212                 it = ImageType.UNKNOWN;
213         }
214
215         return it;
216     }
217
218     public @NonNull String getAlbumArtMimeType() {
219         String mimeType = "";
220         switch (imageType) {
221             case BMP:
222                 mimeType = "image/bmp";
223                 break;
224             case JPEG:
225                 mimeType = "image/jpeg";
226                 break;
227             case UNKNOWN:
228             case URL:
229             case NONE:
230             default:
231                 break;
232         }
233         return mimeType;
234     }
235 }