diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/StreamsFragment.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/StreamsFragment.java index 51fd6ee8dd..ad9283beef 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/StreamsFragment.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/StreamsFragment.java @@ -25,6 +25,9 @@ import com.anpmech.mpd.item.Stream; import com.namelessdev.mpdroid.MPDApplication; import com.namelessdev.mpdroid.R; +import com.namelessdev.mpdroid.playlists.Playlist; +import com.namelessdev.mpdroid.playlists.PlaylistEntry; +import com.namelessdev.mpdroid.playlists.Playlists; import com.namelessdev.mpdroid.tools.StreamFetcher; import com.namelessdev.mpdroid.tools.Tools; @@ -110,13 +113,7 @@ public void addEdit(final int idx, final CharSequence streamUrlToAdd) { final View view = factory.inflate(R.layout.stream_dialog, null); final EditText nameEdit = (EditText) view.findViewById(R.id.name_edit); final EditText urlEdit = (EditText) view.findViewById(R.id.url_edit); - final int streamTitle; - - if (idx < 0) { - streamTitle = R.string.addStream; - } else { - streamTitle = R.string.editStream; - } + final int streamTitle = idx < 0 ? R.string.addStream : R.string.editStream; if (idx >= 0 && idx < mUnordered.size()) { final Stream stream = mUnordered.get(idx); @@ -413,7 +410,17 @@ private String getText(final TextView textView) { @Override public void onClick(final DialogInterface dialog, final int which) { final String name = getText(mNameEdit); - final String url = getText(mUrlEdit); + String url = getText(mUrlEdit); + + // if URL is a playlist, use first entry as stream URL + final Playlist playlist = Playlists.create(url); + if (playlist != null) { + final List playlistEntries = playlist.getEntries(); + if (playlistEntries != null && playlistEntries.size() > 0) { + url = playlistEntries.get(0).getUrl(); + } + } + mApp.addConnectionLock(this); if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(url)) { @@ -434,8 +441,7 @@ public void onClick(final DialogInterface dialog, final int which) { } if (mStreamUrlToAdd != null) { - Toast.makeText(getActivity(), R.string.streamSaved, - Toast.LENGTH_SHORT).show(); + Toast.makeText(getActivity(), R.string.streamSaved, Toast.LENGTH_SHORT).show(); } } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/M3UPlaylist.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/M3UPlaylist.java new file mode 100644 index 0000000000..bf04e13a5b --- /dev/null +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/M3UPlaylist.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010-2017 The MPDroid Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.namelessdev.mpdroid.playlists; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +class M3UPlaylist implements Playlist { + + private static final String M3U_HEADER = "#EXTM3U"; + + private static final String M3U_METADATA = "#EXTINF"; + + private final String mUrl; + + M3UPlaylist(final String url) { + this.mUrl = url; + } + + @Override + public List getEntries() { + return Playlists.extractEntries(mUrl, new Playlists.PlaylistEntryExtractor() { + @Override + public List extractEntries(final BufferedReader reader) throws IOException { + final List entries = new ArrayList<>(); + + boolean isExtended = false; + String name = null; + Integer length = null; + + String line; + while ((line = reader.readLine()) != null) { + if (line.toUpperCase().equals(M3U_HEADER)) { + isExtended = true; + continue; + } + if (isExtended && line.toUpperCase().startsWith(M3U_METADATA)) { + final int colonPos = line.indexOf(':'); + final int commaPos = line.indexOf(','); + if (colonPos > 0 && commaPos > 0) { + try { + length = Integer.valueOf(line.substring(colonPos + 1, commaPos)); + name = line.substring(commaPos + 1); + } catch (final NumberFormatException ignore) { + length = null; + name = null; + } + } + continue; + } + entries.add(new PlaylistEntry(line, name, length)); + } + + return entries; + } + }); + } +} diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/PLSPlaylist.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/PLSPlaylist.java new file mode 100644 index 0000000000..04630d52a7 --- /dev/null +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/PLSPlaylist.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010-2017 The MPDroid Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.namelessdev.mpdroid.playlists; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class PLSPlaylist implements Playlist { + + private static final String NUMBER_OF_ENTRIES = "NUMBEROFENTRIES"; + + private static final String FILE = "FILE"; + + private static final String TITLE = "TITLE"; + + private static final String LENGTH = "LENGTH"; + + private final String mUrl; + + PLSPlaylist(final String url) { + this.mUrl = url; + } + + @Override + public List getEntries() { + return Playlists.extractEntries(mUrl, new Playlists.PlaylistEntryExtractor() { + @Override + public List extractEntries(final BufferedReader reader) throws IOException { + final List entries = new ArrayList<>(); + + final Map content = new HashMap<>(); + + String line; + while ((line = reader.readLine()) != null) { + final int divideIndex = line.indexOf('='); + if (divideIndex < 0) { + continue; + } + + final String key = line.substring(0, divideIndex); + final String value = line.substring(divideIndex + 1); + + content.put(key.trim().toUpperCase(), value.trim()); + } + + for (int i = 1; i <= parseInteger(content.get(NUMBER_OF_ENTRIES)); i++) { + final String file = content.get(FILE + i); + if (file == null) { + continue; + } + entries.add(new PlaylistEntry(file, content.get(TITLE + i), + parseInteger(content.get(LENGTH + i)))); + } + + return entries; + } + + private int parseInteger(final String value) { + if (value == null) { + return 0; + } + try { + return Integer.valueOf(value); + } catch (final NumberFormatException ignore) { + return 0; + } + } + }); + } +} diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/Playlist.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/Playlist.java new file mode 100644 index 0000000000..4095bd8de4 --- /dev/null +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/Playlist.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2010-2017 The MPDroid Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.namelessdev.mpdroid.playlists; + +import java.io.Serializable; +import java.util.List; + +public interface Playlist extends Serializable { + + List getEntries(); + +} diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/PlaylistEntry.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/PlaylistEntry.java new file mode 100644 index 0000000000..4ab068098e --- /dev/null +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/PlaylistEntry.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010-2017 The MPDroid Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.namelessdev.mpdroid.playlists; + +import java.io.Serializable; + +public class PlaylistEntry implements Serializable { + + private final String mUrl; + + private String mName; + + private Integer mLength; + + PlaylistEntry(final String url) { + this.mUrl = url; + } + + PlaylistEntry(final String url, final String name, final Integer length) { + this(url); + this.mName = name; + this.mLength = length; + } + + + public String getUrl() { + return mUrl; + } + + void setName(final String name) { + this.mName = name; + } + + public String getName() { + return mName; + } + + void setLength(final Integer length) { + this.mLength = length; + } + + public Integer getLength() { + return mLength; + } +} diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/Playlists.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/Playlists.java new file mode 100644 index 0000000000..f231e45b45 --- /dev/null +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/playlists/Playlists.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010-2017 The MPDroid Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.namelessdev.mpdroid.playlists; + +import android.os.AsyncTask; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class Playlists { + + private static final String TAG = "Playlists"; + + private static final String M3U_EXTENSION = "M3U"; + + private static final String PLS_EXTENSION = "PLS"; + + interface PlaylistEntryExtractor { + List extractEntries(BufferedReader reader) throws IOException; + } + + public static Playlist create(final String url) { + if (url == null) { + return null; + } else if (url.toUpperCase().endsWith(M3U_EXTENSION)) { + return new M3UPlaylist(url); + } else if (url.toUpperCase().endsWith(PLS_EXTENSION)) { + return new PLSPlaylist(url); + } else { + return null; + } + } + + static List extractEntries(final String url, + final PlaylistEntryExtractor extractor) { + try { + return new AsyncTask>() { + @Override + protected List doInBackground(final Void... ignore) { + try { + return loadEntries(url, extractor); + } catch (final IOException e) { + Log.e(TAG, "Failed to load playlist.", e); + return Collections.emptyList(); + } + } + }.execute().get(); + } catch (final InterruptedException | ExecutionException e) { + Log.e(TAG, "Failed to load playlist.", e); + return Collections.emptyList(); + } + } + + private static List loadEntries(final String url, + final PlaylistEntryExtractor extractor) + throws IOException { + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(new URL(url).openStream())); + return extractor.extractEntries(reader); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (final IOException ignore) { + } + } + } + } + +} diff --git a/MPDroid/src/main/res/values/strings.xml b/MPDroid/src/main/res/values/strings.xml index 5dfbf40200..d13eb724fb 100644 --- a/MPDroid/src/main/res/values/strings.xml +++ b/MPDroid/src/main/res/values/strings.xml @@ -204,7 +204,7 @@ Playlist Name Enter name for new playlist: Streams - Add Stream + Add stream Edit stream Stream %s added Loading streams…