001/*
002 * Copyright (c) 2012, the Last.fm Java Project and Committers
003 * All rights reserved.
004 *
005 * Redistribution and use of this software in source and binary forms, with or without modification, are
006 * permitted provided that the following conditions are met:
007 *
008 * - Redistributions of source code must retain the above
009 *   copyright notice, this list of conditions and the
010 *   following disclaimer.
011 *
012 * - Redistributions in binary form must reproduce the above
013 *   copyright notice, this list of conditions and the
014 *   following disclaimer in the documentation and/or other
015 *   materials provided with the distribution.
016 *
017 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
018 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
019 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
020 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
021 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
022 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
023 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
024 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
025 */
026
027package de.umass.lastfm;
028
029import java.util.HashMap;
030import java.util.Locale;
031import java.util.Map;
032
033import de.umass.util.MapUtilities;
034import de.umass.xml.DomElement;
035
036/**
037 * Provides access to the Last.fm radio streaming service.<br/>
038 * Note that you have to be a subscriber (or have a special API key) to use this API.
039 * Official documentation can be found here: <a href="https://www.last.fm/api/radio">https://www.last.fm/api/radio</a>
040 *
041 * @author Janni Kovacs
042 */
043public class Radio {
044
045        private String type;
046        private String stationName;
047        private String stationUrl;
048        private boolean supportsDiscovery;
049
050        private Session session;
051        private int expiry = -1;
052
053        private Radio(Session session) {
054                this.session = session;
055        }
056
057        public String getType() {
058                return type;
059        }
060
061        public String getStationName() {
062                return stationName;
063        }
064
065        public String getStationUrl() {
066                return stationUrl;
067        }
068
069        public boolean supportsDiscovery() {
070                return supportsDiscovery;
071        }
072
073        /**
074         * Returns the playlist expiration value for the last playlist fetchet, or -1 if no playlist has been fetched yet.
075         *
076         * @return playlist expiration in seconds
077         */
078        public int playlistExpiresIn() {
079                return expiry;
080        }
081
082        /**
083         * Resolve the name of a resource into a station depending on which resource it is most likely to represent
084         *
085         * @param name The tag or artist to resolve
086         * @param apiKey A Last.fm API key.
087         * @return A {@link RadioStation} or <code>null</code>
088         */
089        public static RadioStation search(String name, String apiKey) {
090                Result result = Caller.getInstance().call("radio.search", apiKey, "name", name);
091                if(!result.isSuccessful())
092                        return null;
093
094                DomElement stationElement = result.getContentElement().getChild("station");
095                if(stationElement == null)
096                        return null;
097
098                return new RadioStation(stationElement.getChildText("url"), stationElement.getChildText("name"));
099        }
100
101        /**
102         * Tune in to a Last.fm radio station.
103         *
104         * @param station An instance of {@link RadioStation}
105         * @param session A Session instance
106         * @return a Radio instance
107         */
108        public static Radio tune(RadioStation station, Session session) {
109                return tune(station, Locale.getDefault(), session);
110        }
111
112        /**
113         * Tune in to a Last.fm radio station.
114         *
115         * @param station An instance of {@link RadioStation}
116         * @param locale The language you want the radio's name in
117         * @param session A Session instance
118         * @return a Radio instance
119         */
120        public static Radio tune(RadioStation station, Locale locale, Session session) {
121                return tune(station.getUrl(), locale, session);
122        }
123
124        /**
125         * Tune in to a Last.fm radio station.
126         *
127         * @param station A lastfm radio URL
128         * @param locale The language you want the radio's name in
129         * @param session A Session instance
130         * @return a Radio instance
131         */
132        public static Radio tune(String station, Locale locale, Session session) {
133                Map<String, String> params = new HashMap<String, String>();
134                params.put("station", station);
135                if (locale != null && locale.getLanguage().length() != 0) {
136                        params.put("lang", locale.getLanguage());
137                }
138
139                Result result = Caller.getInstance().call("radio.tune", session, params);
140                if (!result.isSuccessful())
141                        return null;
142
143                DomElement root = result.getContentElement();
144                Radio radio = new Radio(session);
145                radio.type = root.getChildText("type");
146                radio.stationName = root.getChildText("name");
147                radio.stationUrl = root.getChildText("url");
148                radio.supportsDiscovery = "1".equals(root.getChildText("type"));
149                return radio;
150        }
151
152        /**
153         * Fetches a new radio playlist or <code>null</code> if an error occured, such as when the user is not allowed to stream radio
154         * (no subscriber).
155         *
156         * @return a new {@link Playlist} or <code>null</code>
157         */
158        public Playlist getPlaylist() {
159                return getPlaylist(false, false);
160        }
161
162        /**
163         * Fetches a new radio playlist.
164         *
165         * @param discovery Whether to request last.fm content with discovery mode switched on
166         * @param rtp Whether the user is scrobbling or not during this radio session (helps content generation)
167         * @return a new Playlist
168         */
169        public Playlist getPlaylist(boolean discovery, boolean rtp) {
170                return getPlaylist(discovery, rtp, false, -1, -1);
171        }
172
173        /**
174         * Fetches a new radio playlist.
175         *
176         * @param discovery Whether to request last.fm content with discovery mode switched on
177         * @param rtp Whether the user is scrobbling or not during this radio session (helps content generation)
178         * @param buyLinks Whether the response should contain links for purchase/download, if available
179         * @param speedMultiplier The rate at which to provide the stream (supported multipliers are 1.0 and 2.0)
180         * @param bitrate What bitrate to stream content at, in kbps (supported bitrates are 64 and 128)
181         * @return a new Playlist
182         */
183        public Playlist getPlaylist(boolean discovery, boolean rtp, boolean buyLinks, double speedMultiplier, int bitrate) {
184                Map<String, String> params = new HashMap<String, String>();
185                params.put("discovery", String.valueOf(discovery));
186                params.put("rtp", String.valueOf(rtp));
187                params.put("buylinks", String.valueOf(buyLinks));
188                MapUtilities.nullSafePut(params, "speed_multiplier", speedMultiplier);
189                MapUtilities.nullSafePut(params, "bitrate", bitrate);
190                Result result = Caller.getInstance().call("radio.getPlaylist", session, params);
191
192                if (!result.isSuccessful())
193                        return null;
194
195                DomElement root = result.getContentElement();
196                for (DomElement e : root.getChildren("link")) {
197                        if ("http://www.last.fm/expiry".equals(e.getAttribute("rel"))) {
198                                this.expiry = Integer.parseInt(e.getText());
199                                break;
200                        }
201                }
202                return ResponseBuilder.buildItem(root, Playlist.class);
203        }
204
205        public static class RadioStation {
206                private String name;
207                private String url;
208
209                public RadioStation(String url) {
210                        this(url, null);
211                }
212
213                public RadioStation(String url, String name) {
214                        this.url = url;
215                        this.name = name;
216                }
217
218                public String getUrl() {
219                        return url;
220                }
221
222                public String getName() {
223                        return name;
224                }
225
226                public static RadioStation similarArtists(String artist) {
227                        return new RadioStation("lastfm://artist/" + artist + "/similarartists");
228                }
229
230                public static RadioStation artistFans(String artist) {
231                        return new RadioStation("lastfm://artist/" + artist + "/fans");
232                }
233
234                public static RadioStation library(String user) {
235                        return new RadioStation("lastfm://user/" + user + "/library");
236                }
237
238                public static RadioStation neighbours(String user) {
239                        return new RadioStation("lastfm://user/" + user + "/neighbours");
240                }
241
242                /**
243                 * @deprecated This station has been deprected as of nov. 2010, see <a href="https://www.last.fm/stationchanges2010">here</a>
244                 */
245                public static RadioStation lovedTracks(String user) {
246                        return new RadioStation("lastfm://user/" + user + "/loved");
247                }
248
249                public static RadioStation recommended(String user) {
250                        return new RadioStation("lastfm://user/" + user + "/recommended");
251                }
252
253                public static RadioStation tagged(String tag) {
254                        return new RadioStation("lastfm://globaltags/" + tag);
255                }
256
257                /**
258                 * @deprecated This station has been deprected as of nov. 2010, see <a href="https://www.last.fm/stationchanges2010">here</a>
259                 */
260                public static RadioStation playlist(String playlistId) {
261                        return new RadioStation("lastfm://playlist/" + playlistId);
262                }
263
264                /**
265                 * @deprecated This station has been deprected as of nov. 2010, see <a href="https://www.last.fm/stationchanges2010">here</a>
266                 */
267                public static RadioStation personalTag(String user, String tag) {
268                        return new RadioStation("lastfm://usertags/" + user + "/" + tag);
269                }
270        }
271}