Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streams: Add support for playlist URLs #834

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<PlaylistEntry> playlistEntries = playlist.getEntries();
if (playlistEntries != null && playlistEntries.size() > 0) {
url = playlistEntries.get(0).getUrl();
}
}

mApp.addConnectionLock(this);

if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(url)) {
Expand All @@ -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();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<PlaylistEntry> getEntries() {
return Playlists.extractEntries(mUrl, new Playlists.PlaylistEntryExtractor() {
@Override
public List<PlaylistEntry> extractEntries(final BufferedReader reader) throws IOException {
final List<PlaylistEntry> 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;
}
});
}
}
Original file line number Diff line number Diff line change
@@ -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<PlaylistEntry> getEntries() {
return Playlists.extractEntries(mUrl, new Playlists.PlaylistEntryExtractor() {
@Override
public List<PlaylistEntry> extractEntries(final BufferedReader reader) throws IOException {
final List<PlaylistEntry> entries = new ArrayList<>();

final Map<String, String> 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;
}
}
});
}
}
Original file line number Diff line number Diff line change
@@ -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<PlaylistEntry> getEntries();

}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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<PlaylistEntry> 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<PlaylistEntry> extractEntries(final String url,
final PlaylistEntryExtractor extractor) {
try {
return new AsyncTask<Void, Void, List<PlaylistEntry>>() {
@Override
protected List<PlaylistEntry> 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<PlaylistEntry> 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) {
}
}
}
}

}
Loading