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