Merge pull request #72 from medavox/master

Code cleanup & minor additions
This commit is contained in:
Christian Schabesberger 2015-11-03 17:54:09 +01:00
commit c51a5a51f1
14 changed files with 86 additions and 106 deletions

3
.gitignore vendored
View file

@ -1,3 +1,4 @@
.gitignore
.gradle
/local.properties
/.idea/workspace.xml
@ -5,3 +6,5 @@
.DS_Store
/build
/captures
.idea/gradle.xml
.idea/misc.xml

2
app/.gitignore vendored
View file

@ -1 +1,3 @@
.gitignore
/build
app/app.iml

View file

@ -68,6 +68,7 @@
<activity android:name=".PlayVideoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/FullscreenTheme"
android:parentActivityName=".VideoItemDetailActivity"
>
</activity>
<activity

View file

@ -4,6 +4,7 @@ import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
/**
* Created by Christian Schabesberger on 14.08.15.
@ -45,7 +46,13 @@ public class Downloader {
response.append(inputLine);
}
in.close();
} catch (Exception e) {
}
catch(UnknownHostException uhe) {//thrown when there's no internet connection
uhe.printStackTrace();
//Toast.makeText(getActivity(), uhe.getMessage(), Toast.LENGTH_LONG).show();
}
catch (Exception e) {
e.printStackTrace();
}
return response.toString();

View file

@ -25,11 +25,11 @@ public interface StreamingService {
public String name = "";
}
ServiceInfo getServiceInfo();
Class getExtractorClass();
Class getSearchEngineClass();
Extractor getExtractorInstance();
SearchEngine getSearchEngineInstance();
// When a VIEW_ACTION is caught this function will test if the url delivered within the calling
// Intent was meant to be watched with this Service.
// Return false if this service shall not allow to be callean through ACTIONs.
/**When a VIEW_ACTION is caught this function will test if the url delivered within the calling
Intent was meant to be watched with this Service.
Return false if this service shall not allow to be callean through ACTIONs.*/
boolean acceptUrl(String videoUrl);
}

View file

@ -59,7 +59,7 @@ public class VideoInfo {
public static final int VIDEO_AVAILABLE = 0x00;
public static final int VIDEO_UNAVAILABLE = 0x01;
public static final int VIDEO_UNAVAILABLE_GEMA = 0x02;
public static final int VIDEO_UNAVAILABLE_GEMA = 0x02;//German DRM organisation; sound pretty draconian
public static String getNameById(int id) {
switch(id) {
@ -68,8 +68,7 @@ public class VideoInfo {
case I_WEBM: return F_WEBM;
case I_M4A: return F_M4A;
case I_WEBMA: return F_WEBMA;
default: Log.e(TAG, "format not known: " +
Integer.toString(id) + "call the programmer he messed it up.");
default: formatNotKnown(id);
}
return "";
}
@ -81,8 +80,7 @@ public class VideoInfo {
case I_WEBM: return C_WEBM;
case I_M4A: return C_M4A;
case I_WEBMA: return C_WEBMA;
default: Log.e(TAG, "format not known: " +
Integer.toString(id) + "call the programmer he messed it up.");
default: formatNotKnown(id);
}
return "";
}
@ -94,8 +92,7 @@ public class VideoInfo {
case I_WEBM: return M_WEBM;
case I_M4A: return M_M4A;
case I_WEBMA: return M_WEBMA;
default: Log.e(TAG, "format not known: " +
Integer.toString(id) + "call the programmer he messed it up.");
default: formatNotKnown(id);
}
return "";
}
@ -109,6 +106,11 @@ public class VideoInfo {
public String resolution = "";
}
protected static void formatNotKnown(int id) {
Log.e(TAG, "format not known: \"" +
Integer.toString(id) + "\". Call the programmers, they messed it up!");
}
public static class AudioStream {
public AudioStream(String url, int format, int bandwidth, int samplingRate) {
this.url = url; this.format = format;

View file

@ -61,7 +61,7 @@ public class VideoInfoItemViewCreator {
if(!info.upload_date.isEmpty()) {
holder.itemUploadDateView.setText(info.upload_date);
} else {
//tewak if nececeary: This is a hack preventing to have a white space in the layout :P
//tweak if necessary: This is a hack to prevent having white space in the layout :P
holder.itemUploadDateView.setText(info.view_count);
}

View file

@ -67,8 +67,8 @@ public class VideoItemDetailActivity extends AppCompatActivity {
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, i);
try {
currentStreamingService = i;
extractor = (Extractor) ServiceList.getService(i)
.getExtractorClass().newInstance();
extractor = ServiceList.getService(i)
.getExtractorInstance();
} catch (Exception e) {
e.printStackTrace();
}
@ -122,9 +122,9 @@ public class VideoItemDetailActivity extends AppCompatActivity {
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
Intent intent = new Intent(this, VideoItemListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
NavUtils.navigateUpTo(this, intent);

View file

@ -79,16 +79,16 @@ public class VideoItemDetailFragment extends Fragment {
private class ExtractorRunnable implements Runnable {
private Handler h = new Handler();
private Class extractorClass;
private Extractor extractor;
private String videoUrl;
public ExtractorRunnable(String videoUrl, Class extractorClass) {
this.extractorClass = extractorClass;
public ExtractorRunnable(String videoUrl, Extractor extractor, VideoItemDetailFragment f) {
this.extractor = extractor;
this.videoUrl = videoUrl;
}
@Override
public void run() {
try {
Extractor extractor = (Extractor) extractorClass.newInstance();
VideoInfo videoInfo = extractor.getVideoInfo(videoUrl);
h.post(new VideoResultReturnedRunnable(videoInfo));
if (videoInfo.videoAvailableStatus == VideoInfo.VIDEO_AVAILABLE) {
@ -173,7 +173,7 @@ public class VideoItemDetailFragment extends Fragment {
}
} catch (java.lang.NullPointerException e) {
// No god programm design i know. :/
// Not good program design, I know. :/
Log.w(TAG, "updateThumbnail(): Fragment closed before thread ended work");
}
}
@ -324,7 +324,8 @@ public class VideoItemDetailFragment extends Fragment {
StreamingService streamingService = ServiceList.getService(
getArguments().getInt(STREAMING_SERVICE));
extractorThread = new Thread(new ExtractorRunnable(
getArguments().getString(VIDEO_URL), streamingService.getExtractorClass()));
getArguments().getString(VIDEO_URL), streamingService.getExtractorInstance(), this));
autoPlayEnabled = getArguments().getBoolean(AUTO_PLAY);
extractorThread.start();
} catch (Exception e) {

View file

@ -106,6 +106,7 @@ public class VideoItemListActivity extends AppCompatActivity
//-------- remove this line when multiservice support is implemented ----------
currentStreamingServiceId = ServiceList.getIdOfService("Youtube");
//-----------------------------------------------------------------------------
listFragment = (VideoItemListFragment) getSupportFragmentManager()
.findFragmentById(R.id.videoitem_list);
listFragment.setStreamingService(ServiceList.getService(currentStreamingServiceId));

View file

@ -72,14 +72,14 @@ public class VideoItemListFragment extends ListFragment {
}
private class SearchRunnable implements Runnable {
private Class engineClass = null;
private SearchEngine engine;
private String query;
private int page;
Handler h = new Handler();
private volatile boolean run = true;
private int requestId;
public SearchRunnable(Class engineClass, String query, int page, int requestId) {
this.engineClass = engineClass;
public SearchRunnable(SearchEngine engine, String query, int page, int requestId) {
this.engine = engine;
this.query = query;
this.page = page;
this.requestId = requestId;
@ -89,13 +89,6 @@ public class VideoItemListFragment extends ListFragment {
}
@Override
public void run() {
SearchEngine engine = null;
try {
engine = (SearchEngine) engineClass.newInstance();
} catch(Exception e) {
e.printStackTrace();
return;
}
try {
SearchEngine.Result result = engine.search(query, page);
if(run) {
@ -197,7 +190,8 @@ public class VideoItemListFragment extends ListFragment {
private void startSearch(String query, int page) {
currentRequestId++;
terminateThreads();
searchRunnable = new SearchRunnable(streamingService.getSearchEngineClass(), query, page, currentRequestId);
searchRunnable = new SearchRunnable(streamingService.getSearchEngineInstance(),
query, page, currentRequestId);
searchThread = new Thread(searchRunnable);
searchThread.start();
}
@ -252,10 +246,6 @@ public class VideoItemListFragment extends ListFragment {
}
}
void displayList() {
}
/**
* The serialization (saved instance state) Bundle key representing the
* activated item position. Only used on tablets.

View file

@ -3,6 +3,7 @@ package org.schabi.newpipe.youtube;
import android.util.Log;
import android.util.Xml;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
@ -50,7 +51,7 @@ public class YoutubeExtractor implements Extractor {
private static final String TAG = YoutubeExtractor.class.toString();
// These lists only contain itag formats that are supported by the common Android Video player.
// How ever if you are heading for a list showing all itag formats lock at
// How ever if you are heading for a list showing all itag formats look at
// https://github.com/rg3/youtube-dl/issues/1687
public static int resolveFormat(int itag) {
@ -141,14 +142,7 @@ public class YoutubeExtractor implements Extractor {
Document doc = Jsoup.parse(site, siteUrl);
try {
Pattern p = Pattern.compile("v=([0-9a-zA-Z]*)");
Matcher m = p.matcher(siteUrl);
m.find();
videoInfo.id = m.group(1);
} catch (Exception e) {
e.printStackTrace();
}
videoInfo.id = matchGroup1("v=([0-9a-zA-Z]*)", siteUrl);
videoInfo.age_limit = 0;
videoInfo.webpage_url = siteUrl;
@ -160,16 +154,14 @@ public class YoutubeExtractor implements Extractor {
JSONObject ytAssets = null;
String dashManifest;
{
Pattern p = Pattern.compile("ytplayer.config\\s*=\\s*(\\{.*?\\});");
Matcher m = p.matcher(site);
m.find();
String jsonString = matchGroup1("ytplayer.config\\s*=\\s*(\\{.*?\\});", site);
try {
playerArgs = (new JSONObject(m.group(1)))
.getJSONObject("args");
ytAssets = (new JSONObject(m.group(1)))
.getJSONObject("assets");
}catch (Exception e) {
JSONObject jsonObj = new JSONObject(jsonString);
playerArgs = jsonObj.getJSONObject("args");
ytAssets = jsonObj.getJSONObject("assets");
}
catch (Exception e) {
e.printStackTrace();
// If we fail in this part the video is most likely not available.
// Determining why is done later.
@ -199,7 +191,7 @@ public class YoutubeExtractor implements Extractor {
videoInfo.audioStreams = parseDashManifest(dashManifest, decryptionCode);
} catch (Exception e) {
//todo: check if the following statement is true
Log.e(TAG, "Dash manifest seems not to bee available.");
Log.e(TAG, "Dash manifest doesn't seem to be available.");
e.printStackTrace();
}
@ -263,15 +255,9 @@ public class YoutubeExtractor implements Extractor {
// upload date
videoInfo.upload_date = doc.select("strong[class=\"watch-time-text\"").first()
.text();
// Try to only use date not the text around it
try {
Pattern p = Pattern.compile("([0-9.]*$)");
Matcher m = p.matcher(videoInfo.upload_date);
m.find();
videoInfo.upload_date = m.group(1);
} catch (Exception e) {
e.printStackTrace();
}
videoInfo.upload_date = matchGroup1("([0-9.]*$)", videoInfo.upload_date);
// description
videoInfo.description = doc.select("p[id=\"eow-description\"]").first()
@ -322,16 +308,9 @@ public class YoutubeExtractor implements Extractor {
private VideoInfo.AudioStream[] parseDashManifest(String dashManifest, String decryptoinCode) {
if(!dashManifest.contains("/signature/")) {
String encryptedSig = "";
String encryptedSig = matchGroup1("/s/([a-fA-F0-9\\.]+)", dashManifest);
String decryptedSig;
try {
Pattern p = Pattern.compile("/s/([a-fA-F0-9\\.]+)");
Matcher m = p.matcher(dashManifest);
m.find();
encryptedSig = m.group(1);
} catch (Exception e) {
e.printStackTrace();
}
decryptedSig = decryptSignature(encryptedSig, decryptoinCode);
dashManifest = dashManifest.replace("/s/" + encryptedSig, "/signature/" + decryptedSig);
}
@ -396,10 +375,7 @@ public class YoutubeExtractor implements Extractor {
info.webpage_url = li.select("a[class*=\"content-link\"]").first()
.attr("abs:href");
try {
Pattern p = Pattern.compile("v=([0-9a-zA-Z-]*)");
Matcher m = p.matcher(info.webpage_url);
m.find();
info.id=m.group(1);
info.id = matchGroup1("v=([0-9a-zA-Z-]*)", info.webpage_url);
} catch (Exception e) {
e.printStackTrace();
}
@ -446,27 +422,15 @@ public class YoutubeExtractor implements Extractor {
String decryptionCode;
try {
Pattern p = Pattern.compile("\\.sig\\|\\|([a-zA-Z0-9$]+)\\(");
Matcher m = p.matcher(playerCode);
m.find();
decryptionFuncName = m.group(1);
decryptionFuncName = matchGroup1("\\.sig\\|\\|([a-zA-Z0-9$]+)\\(", playerCode);
String functionPattern = "(function " + decryptionFuncName.replace("$", "\\$") + "\\([a-zA-Z0-9_]*\\)\\{.+?\\})";
p = Pattern.compile(functionPattern);
m = p.matcher(playerCode);
m.find();
decryptionFunc = m.group(1);
decryptionFunc = matchGroup1(functionPattern, playerCode);
p = Pattern.compile(";([A-Za-z0-9_\\$]{2})\\...\\(");
m = p.matcher(decryptionFunc);
m.find();
helperObjectName = m.group(1);
helperObjectName = matchGroup1(";([A-Za-z0-9_\\$]{2})\\...\\(", decryptionFunc);
String helperPattern = "(var " + helperObjectName.replace("$", "\\$") + "=\\{.+?\\}\\};)function";
p = Pattern.compile(helperPattern);
m = p.matcher(playerCode);
m.find();
helperObject = m.group(1);
helperObject = matchGroup1(helperPattern, playerCode);
} catch (Exception e) {
e.printStackTrace();
@ -493,4 +457,11 @@ public class YoutubeExtractor implements Extractor {
Context.exit();
return result.toString();
}
private String matchGroup1(String pattern, String input) {
Pattern pat = Pattern.compile(pattern);
Matcher mat = pat.matcher(input);
mat.find();
return mat.group(1);
}
}

View file

@ -57,14 +57,14 @@ public class YoutubeSearchEngine implements SearchEngine {
int i = 0;
for(Element item : list.children()) {
i++;
/* First we need to determine witch kind of item we are working with.
Youtube depicts fife different kinds if items at its search result page. These are
regular videos, playlists, channels, two types of video suggestions, and a no video
found item. Since we only want videos, we net to filter out all the others.
/* First we need to determine which kind of item we are working with.
Youtube depicts five different kinds of items on its search result page. These are
regular videos, playlists, channels, two types of video suggestions, and a "no video
found" item. Since we only want videos, we need to filter out all the others.
An example for this can be seen here:
https://www.youtube.com/results?search_query=asdf&page=1
We already applied a filter to the url, so we don't need to care about channels, and
We already applied a filter to the url, so we don't need to care about channels and
playlists now.
*/
@ -102,9 +102,9 @@ public class YoutubeSearchEngine implements SearchEngine {
Element te = item.select("div[class=\"yt-thumb video-thumb\"]").first()
.select("img").first();
resultItem.thumbnail_url = te.attr("abs:src");
// Sometimes youtube sends links to gif files witch somehow seam to not exist
// Sometimes youtube sends links to gif files which somehow seem to not exist
// anymore. Items with such gif also offer a secondary image source. So we are going
// to use that if we caught such an item.
// to use that if we've caught such an item.
if(resultItem.thumbnail_url.contains(".gif")) {
resultItem.thumbnail_url = te.attr("abs:data-thumb");
}
@ -113,7 +113,6 @@ public class YoutubeSearchEngine implements SearchEngine {
Log.e(TAG, "GREAT FUCKING ERROR");
}
}
return result;
}
}

View file

@ -1,6 +1,9 @@
package org.schabi.newpipe.youtube;
import org.schabi.newpipe.StreamingService;
import org.schabi.newpipe.Extractor;
import org.schabi.newpipe.SearchEngine;
/**
* Created by Christian Schabesberger on 23.08.15.
@ -30,12 +33,12 @@ public class YoutubeService implements StreamingService {
return serviceInfo;
}
@Override
public Class getExtractorClass() {
return YoutubeExtractor.class;
public Extractor getExtractorInstance() {
return new YoutubeExtractor();
}
@Override
public Class getSearchEngineClass() {
return YoutubeSearchEngine.class;
public SearchEngine getSearchEngineInstance() {
return new YoutubeSearchEngine();
}
@Override
public boolean acceptUrl(String videoUrl) {