1
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-09-20 23:00:50 +02:00

Compare commits

..

113 Commits

Author SHA1 Message Date
Christian Schabesberger
54c32b9fe2 moved on to version 0.8.0:
* moved on to version 0.8.0
* removed posibility to use *.jar files
* updated dependend libraries
2016-06-12 18:24:16 +02:00
David
9d5951765f Fix for #310 2016-06-08 12:35:54 -03:00
David
ddc3b47dfa Fix for #308 part 2 2016-06-07 11:43:13 -03:00
David
59523d6a08 Fix for #308 2016-06-07 11:32:31 -03:00
Christian Schabesberger
686f395158 Merge pull request #307 from mingchen/master
Upgrade gradle wrapper to 2.10 and fix compile errors.
2016-06-04 10:54:12 +02:00
Ming Chen
f7b7340b30 Fix compile error due to improper define of xmlns 2016-06-03 16:13:30 -07:00
Ming Chen
9bcdad0218 Upgrade wrapper to 2.10 2016-06-03 15:53:02 -07:00
Matej U
ff4601f487 Translated using Weblate (Slovenian)
Currently translated at 100.0% (133 of 133 strings)
2016-06-03 10:49:16 +02:00
Renan Souza do Nascimento
535cebde51 Translated using Weblate (Portuguese)
Currently translated at 99.2% (132 of 133 strings)
2016-05-31 00:45:17 +02:00
Weblate
ed23faa455 Merge remote-tracking branch 'origin/master' 2016-05-27 15:29:12 +02:00
naofum
0c695d721d Translated using Weblate (Japanese)
Currently translated at 100.0% (133 of 133 strings)
2016-05-27 15:29:12 +02:00
Benedikt Freisen
d7a208dcee Translated using Weblate (German)
Currently translated at 91.7% (122 of 133 strings)
2016-05-27 15:29:11 +02:00
Christian Schabesberger
3eb2a26e4e blub commit 2016-05-27 15:29:02 +02:00
Mladen Pejaković
cc2d365e5a Translated using Weblate (Serbian)
Currently translated at 100.0% (133 of 133 strings)
2016-05-26 22:41:03 +02:00
naofum
76ac2cc58e Translated using Weblate (Japanese)
Currently translated at 100.0% (133 of 133 strings)
2016-05-26 12:44:04 +02:00
Christian Schabesberger
aee26fcffc add downloads menu item to detail fragment 2016-05-26 00:11:42 +02:00
Christian Schabesberger
685eebeb56 add title to activitys 2016-05-25 23:51:22 +02:00
Christian Schabesberger
f23ae091cc add icons for progress tiles 2016-05-25 23:35:27 +02:00
Christian Schabesberger
1421dca35f clean up gigaget code 2016-05-25 23:08:15 +02:00
Christian Schabesberger
39c2f31a22 changed color of download blocks 2016-05-25 22:59:32 +02:00
Christian Schabesberger
239ef1c238 add action for downloads menu item 2016-05-25 22:34:36 +02:00
Christian Schabesberger
466ba93750 Merge branch 'master' of github.com:theScrabi/NewPipe 2016-05-25 22:19:47 +02:00
Marian Hanzel
72007a0162 Translated using Weblate (Slovak)
Currently translated at 100.0% (129 of 129 strings)
2016-05-25 19:50:47 +02:00
David
26c0445b83 Fix for #273 2016-05-25 08:27:15 -03:00
Christian Schabesberger
6f6c1704d4 add downloads menu item 2016-05-24 14:06:49 +02:00
Weblate
14aa6de422 Merge remote-tracking branch 'origin/master' 2016-05-24 13:51:33 +02:00
Mladen Pejaković
eeb770ffe3 Translated using Weblate (Serbian)
Currently translated at 100.0% (129 of 129 strings)
2016-05-24 13:51:33 +02:00
David
382ac3470b Fix for #298 2016-05-24 08:51:08 -03:00
naofum
3abfb08090 Translated using Weblate (Japanese)
Currently translated at 100.0% (129 of 129 strings)
2016-05-23 13:24:57 +02:00
Matej U
b4aac839c9 Translated using Weblate (Slovenian)
Currently translated at 100.0% (129 of 129 strings)
2016-05-22 22:29:22 +02:00
YFdyh000
da984c23df Translated using Weblate (Simplified Chinese)
Currently translated at 100.0% (129 of 129 strings)
2016-05-22 16:14:29 +02:00
Mladen Pejaković
cab770159d Translated using Weblate (Serbian)
Currently translated at 100.0% (129 of 129 strings)
2016-05-22 13:56:29 +02:00
Christian Schabesberger
7c2ff977d8 Merge branch 'download-manager' of git://github.com/satiricon/NewPipe into satiricon-download-manager 2016-05-22 12:47:24 +02:00
Christian Schabesberger
5bd9334f8f Merge branch 'master' of github.com:theScrabi/NewPipe 2016-05-22 12:44:41 +02:00
Christian Schabesberger
c5544df64c made StreamExtractor use StreamPreviewInfoCollector 2016-05-22 12:39:57 +02:00
Christian Schabesberger
c85e3c07d6 Merge pull request #287 from prashantkhurana/fix/Media-controls-on-main-screen
Added media volume control on main screen Fixes #38
2016-05-22 09:18:23 +02:00
Christian Schabesberger
674e1c0519 Merge pull request #294 from prashantkhurana/Feature/Hide-uploader-bug
Fix to hide uploader correctly
2016-05-22 09:16:37 +02:00
Prashant Khurana
7154a1edb8 Fix to hide uploader correctly 2016-05-15 22:20:28 -04:00
Licaon Kter
be1252e0e6 Translated using Weblate (Romanian)
Currently translated at 64.8% (70 of 108 strings)
2016-05-12 15:45:25 +02:00
Christian Schabesberger
a4ce6c707c update support lib 2016-05-10 00:26:32 +02:00
David
affce74b84 Added menu to download activity 2016-05-02 10:47:18 -03:00
David
730de4061f deleted some unused code and assets 2016-05-02 10:47:18 -03:00
David
3beafa2a74 Added new download dialog 2016-05-02 10:47:18 -03:00
David
c622923edd Fixed the download dialog. Next take it to the previous activity 2016-05-02 10:47:18 -03:00
David
6940021293 Fixed dir settings 2016-05-02 10:47:18 -03:00
David
0156a4f39e Added back button to download activity ActionBar 2016-05-02 10:47:18 -03:00
David
4bae12aa55 Embedded GigaGet download manager. First try. 2016-05-02 10:47:18 -03:00
M2ck
f08b1224c9 Translated using Weblate (French)
Currently translated at 100.0% (108 of 108 strings)
2016-05-02 15:44:32 +02:00
Prashant Khurana
477355db8f Added media volume control on main screen Fixes #38 2016-05-01 21:49:14 -04:00
Allan Nordhøy
11c89165c5 Translated using Weblate (Norwegian Bokmål)
Currently translated at 92.5% (100 of 108 strings)
2016-05-01 00:44:56 +02:00
riotism
825f28ab9a Translated using Weblate (Chinese (Hong Kong))
Currently translated at 92.5% (100 of 108 strings)
2016-04-28 00:44:12 +02:00
Marian Hanzel
54ff5e1dc6 Translated using Weblate (Slovak)
Currently translated at 100.0% (108 of 108 strings)
2016-04-27 18:45:19 +02:00
Gian Maria Viglianti
9b46418628 Translated using Weblate (Italian)
Currently translated at 100.0% (108 of 108 strings)
2016-04-25 13:12:42 +02:00
Benedikt Freisen
04597a9faf Translated using Weblate (German)
Currently translated at 100.0% (108 of 108 strings)
2016-04-18 21:24:55 +02:00
naofum
5b0994dc85 Translated using Weblate (Japanese)
Currently translated at 100.0% (108 of 108 strings)
2016-04-18 15:02:56 +02:00
Christian Schabesberger
7c852c69a3 add queeing videos as an future feature 2016-04-17 15:49:35 +02:00
devnoname120
df47e94ceb Translated using Weblate (French)
Currently translated at 100.0% (108 of 108 strings)
2016-04-17 03:44:30 +02:00
Sérgio Marques
3b68004005 Translated using Weblate (Portuguese)
Currently translated at 100.0% (108 of 108 strings)
2016-04-16 18:39:29 +02:00
devnoname120
5a9c327938 Translated using Weblate (French)
Currently translated at 99.0% (108 of 108 strings)
2016-04-15 01:10:28 +02:00
M2ck
3e31e9783c Translated using Weblate (French)
Currently translated at 99.0% (107 of 108 strings)
2016-04-15 01:10:25 +02:00
Yoo
46fb3639ec Translated using Weblate (Hungarian)
Currently translated at 99.0% (107 of 108 strings)
2016-04-14 18:44:40 +02:00
Rom1
a6eba57099 Translated using Weblate (French)
Currently translated at 99.0% (107 of 108 strings)
2016-04-13 14:49:46 +02:00
M2ck
c8481f961a Translated using Weblate (French)
Currently translated at 98.1% (106 of 108 strings)
2016-04-13 14:49:07 +02:00
Robin van der Vliet
56329f43fb Translated using Weblate (Esperanto)
Currently translated at 71.2% (77 of 108 strings)
2016-04-11 18:44:19 +02:00
Matej U
793fbfb5be Translated using Weblate (Slovenian)
Currently translated at 100.0% (108 of 108 strings)
2016-04-09 20:51:20 +02:00
Rom1
1d8334b762 Translated using Weblate (French)
Currently translated at 93.5% (101 of 108 strings)
2016-04-09 18:44:35 +02:00
Rom1
19330ac415 Translated using Weblate (English)
Currently translated at 100.0% (108 of 108 strings)
2016-04-09 18:44:21 +02:00
Ibrahim Derraz
7a015a0bda Translated using Weblate (French)
Currently translated at 91.6% (99 of 108 strings)
2016-04-07 16:49:27 +02:00
Rom1
f29c422c61 Translated using Weblate (French)
Currently translated at 90.7% (98 of 108 strings)
2016-04-07 16:49:10 +02:00
Ibrahim Derraz
99a369f604 Translated using Weblate (French)
Currently translated at 90.7% (98 of 108 strings)
2016-04-07 16:47:48 +02:00
Rom1
c1f0fb36ac Translated using Weblate (French)
Currently translated at 89.8% (97 of 108 strings)
2016-04-07 16:47:32 +02:00
Ibrahim Derraz
fa0a8905f7 Translated using Weblate (French)
Currently translated at 89.8% (97 of 108 strings)
2016-04-07 16:47:21 +02:00
Rom1
4e63a3269e Translated using Weblate (French)
Currently translated at 88.8% (96 of 108 strings)
2016-04-07 16:46:47 +02:00
Ibrahim Derraz
0f1873e295 Translated using Weblate (French)
Currently translated at 87.9% (95 of 108 strings)
2016-04-07 16:40:26 +02:00
Rom1
23fd28afd5 Translated using Weblate (French)
Currently translated at 86.1% (93 of 108 strings)
2016-04-07 16:38:33 +02:00
naofum
ad5a813a9a Translated using Weblate (Japanese)
Currently translated at 100.0% (108 of 108 strings)
2016-04-07 15:38:19 +02:00
Ibrahim Derraz
f245eedbdf Translated using Weblate (French)
Currently translated at 82.4% (89 of 108 strings)
2016-04-07 15:18:55 +02:00
Rom1
1ce6a6e8c5 Translated using Weblate (French)
Currently translated at 80.5% (87 of 108 strings)
2016-04-07 15:18:38 +02:00
Ibrahim Derraz
0ea7b5526c Translated using Weblate (French)
Currently translated at 80.5% (87 of 108 strings)
2016-04-07 15:18:07 +02:00
Rom1
b41e88f8f3 Translated using Weblate (French)
Currently translated at 79.6% (86 of 108 strings)
2016-04-07 15:17:56 +02:00
Ibrahim Derraz
82b9e79d99 Translated using Weblate (French)
Currently translated at 79.6% (86 of 108 strings)
2016-04-07 15:17:35 +02:00
Rom1
a5383fadb1 Translated using Weblate (French)
Currently translated at 74.0% (80 of 108 strings)
2016-04-07 15:15:24 +02:00
Ibrahim Derraz
bd7fb9bbe4 Translated using Weblate (French)
Currently translated at 73.1% (79 of 108 strings)
2016-04-07 15:15:14 +02:00
Rom1
1ddd0a333c Translated using Weblate (French)
Currently translated at 72.2% (78 of 108 strings)
2016-04-07 15:14:40 +02:00
Ibrahim Derraz
084fec08a6 Translated using Weblate (French)
Currently translated at 72.2% (78 of 108 strings)
2016-04-07 15:14:23 +02:00
Rom1
2b51448b49 Translated using Weblate (French)
Currently translated at 71.2% (77 of 108 strings)
2016-04-07 15:13:14 +02:00
Ibrahim Derraz
f4eee83477 Translated using Weblate (French)
Currently translated at 71.2% (77 of 108 strings)
2016-04-07 15:12:02 +02:00
Rom1
cd622c9e06 Translated using Weblate (French)
Currently translated at 70.3% (76 of 108 strings)
2016-04-07 15:11:52 +02:00
Ibrahim Derraz
7077ebfde2 Translated using Weblate (French)
Currently translated at 70.3% (76 of 108 strings)
2016-04-07 15:11:41 +02:00
Rom1
4862fecb12 Translated using Weblate (French)
Currently translated at 69.4% (75 of 108 strings)
2016-04-07 15:10:58 +02:00
YFdyh000
c189933705 Translated using Weblate (Simplified Chinese)
Currently translated at 100.0% (108 of 108 strings)
2016-04-06 23:14:48 +02:00
Weblate
9c3f7a9139 Merge remote-tracking branch 'origin/master' 2016-04-06 23:12:51 +02:00
Zhaofeng Li
382bf5e936 Translated using Weblate (Simplified Chinese)
Currently translated at 100.0% (108 of 108 strings)
2016-04-06 23:12:51 +02:00
Christian Schabesberger
e2fac962f3 Merge pull request #261 from zhaofengli/ui-strings-ocd-cure
Let's tweak the UI strings a bit
2016-04-06 23:12:47 +02:00
Gian Maria Viglianti
3071ebc0d5 Translated using Weblate (Italian)
Currently translated at 100.0% (108 of 108 strings)
2016-04-06 19:16:15 +02:00
Zhaofeng Li
a5fc6db1fa default_resolution_title: Fix capitalization
"Default Resolution" -> "Default resolution"

See https://www.google.com/design/spec/style/writing.html#writing-capitalization-punctuation
2016-04-06 20:08:29 +08:00
Zhaofeng Li
8060c6a775 strings.xml: Fix grammar
"Download path video" -> "Video download path"
"Download path audio" -> "Audio download path"
2016-04-06 20:06:00 +08:00
Zhaofeng Li
2a6e7f300c search_language_title: Change "Preferable" to "Preferred"
Why? See http://english.stackexchange.com/questions/128996/how-would-one-know-when-to-choose-preferred-or-preferable
2016-04-06 20:02:40 +08:00
Zhaofeng Li
98afe79eaa strings.xml: Remove unnecessary punctuations
Periods, colons and other unnecessary punctuations should not be used in labels.
See https://www.google.com/design/spec/style/writing.html#writing-capitalization-punctuation
2016-04-06 20:02:22 +08:00
halcyonest
a35590f9ea Translated using Weblate (English)
Currently translated at 100.0% (108 of 108 strings)
2016-03-29 03:44:20 +02:00
Marian Hanzel
f14f8c35b8 Translated using Weblate (Slovak)
Currently translated at 100.0% (108 of 108 strings)
2016-03-28 18:06:19 +02:00
Matej U
aa5c510c99 Translated using Weblate (Slovenian)
Currently translated at 100.0% (108 of 108 strings)
2016-03-27 18:57:00 +02:00
Benedikt Freisen
59f33a8def Translated using Weblate (German)
Currently translated at 100.0% (108 of 108 strings)
2016-03-27 16:19:49 +02:00
naofum
dbb4df598c Translated using Weblate (Japanese)
Currently translated at 100.0% (108 of 108 strings)
2016-03-27 03:45:53 +02:00
halcyonest
8452f52da0 Translated using Weblate (Korean)
Currently translated at 100.0% (108 of 108 strings)
2016-03-27 03:40:44 +02:00
Christian Schabesberger
db6613f562 yet another try to fix that test -.- 2016-03-25 20:19:43 +01:00
Christian Schabesberger
44d2775437 Merge branch 'master' of github.com:theScrabi/NewPipe 2016-03-25 20:03:05 +01:00
Christian Schabesberger
a3326dc598 another try to fixe shitty viewcount test 2016-03-25 20:02:50 +01:00
Christian Schabesberger
0cd56ddeb8 Merge branch 'master' of github.com:theScrabi/NewPipe 2016-03-25 19:37:59 +01:00
Christian Schabesberger
2a7f729e73 fixed unit test 2016-03-25 19:37:48 +01:00
Weblate
18301d8f8b Merge remote-tracking branch 'origin/master' 2016-03-25 19:34:47 +01:00
YFdyh000
8286437055 Translated using Weblate (Simplified Chinese)
Currently translated at 100.0% (108 of 108 strings)
2016-03-25 19:34:46 +01:00
YFdyh000
21e8318643 Translated using Weblate (Simplified Chinese)
Currently translated at 100.0% (108 of 108 strings)
2016-03-25 19:06:56 +01:00
90 changed files with 3756 additions and 404 deletions

View File

@@ -55,6 +55,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Subscribe to channels
* Watch videos from a channel
* Search/Watch Playlists
* Queeing videos
* ... and many more
### Multiservice support

View File

@@ -8,8 +8,8 @@ android {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 23
versionCode 17
versionName "0.7.8"
versionCode 18
versionName "0.8.0"
}
buildTypes {
release {
@@ -31,11 +31,10 @@ android {
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:23.2.0'
compile 'com.android.support:support-v4:23.2.0'
compile 'com.android.support:design:23.2.0'
compile 'com.android.support:recyclerview-v7:23.2.0'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:support-v4:23.4.0'
compile 'com.android.support:design:23.4.0'
compile 'com.android.support:recyclerview-v7:23.4.0'
compile 'org.jsoup:jsoup:1.8.3'
compile 'org.mozilla:rhino:1.7.7'
compile 'info.guardianproject.netcipher:netcipher:1.2'
@@ -43,4 +42,6 @@ dependencies {
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
compile 'com.github.nirhart:parallaxscroll:1.0'
compile 'com.google.android.exoplayer:exoplayer:r1.5.5'
compile 'com.google.code.gson:gson:2.4'
compile 'com.nononsenseapps:filepicker:2.0.5'
}

View File

@@ -2,16 +2,15 @@ package org.schabi.newpipe.extractor.youtube;
import android.test.AndroidTestCase;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.schabi.newpipe.extractor.AbstractVideoInfo;
import org.schabi.newpipe.extractor.SearchResult;
import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.StreamPreviewInfo;
import org.schabi.newpipe.extractor.SearchEngine;
import org.schabi.newpipe.extractor.services.youtube.YoutubeSearchEngine;
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.extractor.services.youtube.YoutubeService;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
/**
@@ -44,13 +43,13 @@ public class YoutubeSearchEngineTest extends AndroidTestCase {
SearchEngine engine = ServiceList.getService("Youtube")
.getSearchEngineInstance(new Downloader());
result = engine.search("lefloid",
result = engine.search("star wars",
0, "de", new Downloader()).getSearchResult();
suggestionReply = engine.suggestionList("hello","de",new Downloader());
}
public void testIfNoErrorOccur() {
assertTrue(result.errors.isEmpty() ? "" : ExceptionUtils.getStackTrace(result.errors.get(0))
assertTrue(result.errors.isEmpty() ? "" : getStackTrace(result.errors.get(0))
,result.errors.isEmpty());
}
@@ -97,7 +96,7 @@ public class YoutubeSearchEngineTest extends AndroidTestCase {
// that specific link used for this test, there are no videos with less
// than 10.000 views, so we can test against that.
for(StreamPreviewInfo i : result.resultList) {
assertTrue(i.title + ": " + Long.toString(i.view_count), i.view_count >= 10000);
assertTrue(i.title + ": " + Long.toString(i.view_count), i.view_count >= 1000);
}
}
@@ -118,4 +117,11 @@ public class YoutubeSearchEngineTest extends AndroidTestCase {
assertTrue(s, !s.isEmpty());
}
}
private static String getStackTrace(final Throwable throwable) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}
}

View File

@@ -125,5 +125,17 @@
android:label="@string/general_error"
android:theme="@android:style/Theme.NoDisplay" />
<activity android:name=".ErrorActivity"/>
<!-- giga get related -->
<activity
android:name=".download.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:launchMode="singleTask">
</activity>
<service
android:name="us.shandian.giga.service.DownloadManagerService"/>
</application>
</manifest>

View File

@@ -180,6 +180,12 @@ class ActionBarHandler {
onPlayAudioListener.onActionSelected(selectedVideoStream);
}
return true;
case R.id.menu_item_downloads: {
Intent intent =
new Intent(activity, org.schabi.newpipe.download.MainActivity.class);
activity.startActivity(intent);
return true;
}
default:
Log.e(TAG, "Menu Item not known");
}

View File

@@ -11,6 +11,7 @@ import android.os.Build;
import android.preference.PreferenceManager;
import android.support.design.widget.Snackbar;
import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
@@ -165,7 +166,15 @@ public class ErrorActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_error);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
try {
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle(R.string.error_report_title);
actionBar.setDisplayShowTitleEnabled(true);
} catch (Exception e) {
Log.e(TAG, "Error turing exception handling");
e.printStackTrace();
}
ActivityCommunicator ac = ActivityCommunicator.getCommunicator();
errorList = ac.errorList;

View File

@@ -28,6 +28,8 @@ import android.support.annotation.NonNull;
import java.io.File;
import us.shandian.giga.util.Utility;
/**
* Helper for global settings
*/
@@ -46,10 +48,34 @@ public class NewPipeSettings {
return getFolder(context, R.string.download_path_key, Environment.DIRECTORY_MOVIES);
}
public static String getVideoDownloadPath(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = context.getString(R.string.download_path_key);
String downloadPath = prefs.getString(key, Environment.DIRECTORY_MOVIES);
return downloadPath;
}
public static File getAudioDownloadFolder(Context context) {
return getFolder(context, R.string.download_path_audio_key, Environment.DIRECTORY_MUSIC);
}
public static String getAudioDownloadPath(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = context.getString(R.string.download_path_audio_key);
String downloadPath = prefs.getString(key, Environment.DIRECTORY_MUSIC);
return downloadPath;
}
public static String getDownloadPath(Context context, String fileName)
{
if(Utility.isVideoFile(fileName)) {
return NewPipeSettings.getVideoDownloadPath(context);
}
return NewPipeSettings.getAudioDownloadPath(context);
}
private static File getFolder(Context context, int keyID, String defaultDirectoryName) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = context.getString(keyID);

View File

@@ -56,7 +56,10 @@ public class SettingsActivity extends PreferenceActivity {
getDelegate().onCreate(savedInstanceBundle);
super.onCreate(savedInstanceBundle);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle(R.string.settings_title);
actionBar.setDisplayShowTitleEnabled(true);
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())

View File

@@ -6,12 +6,12 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.extractor.AbstractVideoInfo;
import org.schabi.newpipe.extractor.StreamPreviewInfo;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import org.schabi.newpipe.extractor.AbstractVideoInfo;
import org.schabi.newpipe.extractor.StreamPreviewInfo;
/**
* Created by Christian Schabesberger on 24.10.15.
*
@@ -72,7 +72,7 @@ public class VideoInfoItemViewCreator {
if(info.uploader != null && !info.uploader.isEmpty()) {
holder.itemUploaderView.setText(info.uploader);
} else {
holder.itemDurationView.setVisibility(View.INVISIBLE);
holder.itemUploaderView.setVisibility(View.INVISIBLE);
}
if(info.duration > 0) {
holder.itemDurationView.setText(getDurationString(info.duration));
@@ -88,7 +88,7 @@ public class VideoInfoItemViewCreator {
} else {
holder.itemViewCountView.setVisibility(View.GONE);
}
if(!info.upload_date.isEmpty()) {
if(info.upload_date != null && !info.upload_date.isEmpty()) {
holder.itemUploadDateView.setText(info.upload_date + " • ");
}
@@ -134,9 +134,9 @@ public class VideoInfoItemViewCreator {
if(hours > 0 || !output.isEmpty()) {
if(hours > 0) {
if(hours >= 10 || output.isEmpty()) {
output += Integer.toString(minutes);
output += Integer.toString(hours);
} else {
output += "0" + Integer.toString(minutes);
output += "0" + Integer.toString(hours);
}
} else {
output += "00";

View File

@@ -579,8 +579,7 @@ public class VideoItemDetailFragment extends Fragment {
}
args.putString(DownloadDialog.TITLE, info.title);
DownloadDialog downloadDialog = new DownloadDialog();
downloadDialog.setArguments(args);
DownloadDialog downloadDialog = DownloadDialog.newInstance(args);
downloadDialog.show(activity.getSupportFragmentManager(), "downloadDialog");
} catch (Exception e) {
Toast.makeText(VideoItemDetailFragment.this.getActivity(),

View File

@@ -3,6 +3,7 @@ package org.schabi.newpipe;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
@@ -17,6 +18,7 @@ import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import org.schabi.newpipe.download.MainActivity;
import org.schabi.newpipe.extractor.ExtractionException;
import org.schabi.newpipe.extractor.SearchEngine;
import org.schabi.newpipe.extractor.ServiceList;
@@ -215,8 +217,7 @@ public class VideoItemListActivity extends AppCompatActivity
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
ServiceList.getNameOfService(currentStreamingServiceId), "", R.string.general_error));
}
//-----------------------------------------------------------------------------
//to solve issue 38
setVolumeControlStream(AudioManager.STREAM_MUSIC);
listFragment = (VideoItemListFragment) getSupportFragmentManager()
.findFragmentById(R.id.videoitem_list);
listFragment.setStreamingService(streamingService);
@@ -366,6 +367,11 @@ public class VideoItemListActivity extends AppCompatActivity
"user_report", R.string.user_report));
return true;
}
case R.id.action_show_downloads: {
Intent intent = new Intent(this, org.schabi.newpipe.download.MainActivity.class);
startActivity(intent);
return true;
}
default:
return videoFragment.onOptionsItemSelected(item) ||
super.onOptionsItemSelected(item);

View File

@@ -141,7 +141,8 @@ public class VideoItemListFragment extends ListFragment {
} catch(ExtractionException e) {
ErrorActivity.reportError(h, getActivity(), e, null, null,
ErrorActivity.ErrorInfo.make(ErrorActivity.SEARCHED,
/* todo: this shoudl not be assigned static */ "Youtube", query, R.string.parsing_error));
/* todo: this shoudl not be assigned static */
"Youtube", query, R.string.parsing_error));
//postNewErrorToast(h, R.string.parsing_error);
e.printStackTrace();
@@ -289,7 +290,7 @@ public class VideoItemListFragment extends ListFragment {
if (mode != PRESENT_VIDEOS_MODE
&& list.getChildAt(0) != null
&& list.getLastVisiblePosition() == list.getAdapter().getCount() - 1
&& list.getChildAt(list.getChildCount() - 1).getBottom() <= list.getHeight()) {
&& list.getChildAt(list.getChildCount() - 1).getBottom() >= list.getHeight()) {
long time = System.currentTimeMillis();
if ((time - lastScrollDate) > 200
&& !loadingNextPage) {

File diff suppressed because it is too large Load Diff

View File

@@ -33,13 +33,13 @@ public abstract class SearchEngine {
}
}
private StreamPreviewInfoCollector collector;
private StreamPreviewInfoSearchCollector collector;
public SearchEngine(StreamUrlIdHandler urlIdHandler, int serviceId) {
collector = new StreamPreviewInfoCollector(urlIdHandler, serviceId);
collector = new StreamPreviewInfoSearchCollector(urlIdHandler, serviceId);
}
public StreamPreviewInfoCollector getStreamPreviewInfoCollector() {
protected StreamPreviewInfoSearchCollector getStreamPreviewInfoSearchCollector() {
return collector;
}
@@ -48,7 +48,7 @@ public abstract class SearchEngine {
throws ExtractionException, IOException;
//Result search(String query, int page);
public abstract StreamPreviewInfoCollector search(
public abstract StreamPreviewInfoSearchCollector search(
String query, int page, String contentCountry, Downloader dl)
throws ExtractionException, IOException;
}

View File

@@ -42,6 +42,6 @@ public class SearchResult {
}
public String suggestion = "";
public final List<StreamPreviewInfo> resultList = new Vector<>();
public List<StreamPreviewInfo> resultList = new Vector<>();
public List<Exception> errors = new Vector<>();
}

View File

@@ -29,6 +29,10 @@ import java.util.List;
public abstract class StreamExtractor {
private int serviceId;
private String url;
private StreamUrlIdHandler urlIdHandler;
private Downloader downloader;
private StreamPreviewInfoCollector previewInfoCollector;
public class ExctractorInitException extends ExtractionException {
public ExctractorInitException(String message) {
@@ -51,8 +55,26 @@ public abstract class StreamExtractor {
}
}
public StreamExtractor(String url, Downloader dl, int serviceId) {
public StreamExtractor(StreamUrlIdHandler urlIdHandler, String url, Downloader dl, int serviceId) {
this.serviceId = serviceId;
this.urlIdHandler = urlIdHandler;
previewInfoCollector = new StreamPreviewInfoCollector(urlIdHandler, serviceId);
}
protected StreamPreviewInfoCollector getStreamPreviewInfoCollector() {
return previewInfoCollector;
}
public String getUrl() {
return url;
}
public StreamUrlIdHandler getUrlIdHandler() {
return urlIdHandler;
}
public Downloader getDownloader() {
return downloader;
}
public abstract int getTimeStamp() throws ParsingException;
@@ -72,9 +94,8 @@ public abstract class StreamExtractor {
public abstract String getAverageRating() throws ParsingException;
public abstract int getLikeCount() throws ParsingException;
public abstract int getDislikeCount() throws ParsingException;
public abstract StreamPreviewInfo getNextVideo() throws ParsingException;
public abstract List<StreamPreviewInfo> getRelatedVideos() throws ParsingException;
public abstract StreamUrlIdHandler getUrlIdConverter();
public abstract StreamPreviewInfoExtractor getNextVideo() throws ParsingException;
public abstract StreamPreviewInfoCollector getRelatedVideos() throws ParsingException;
public abstract String getPageUrl();
public abstract StreamInfo.StreamType getStreamType() throws ParsingException;
public int getServiceId() {

View File

@@ -85,7 +85,7 @@ public class StreamInfo extends AbstractVideoInfo {
/* ---- importand data, withoug the video can't be displayed goes here: ---- */
// if one of these is not available an exception is ment to be thrown directly into the frontend.
StreamUrlIdHandler uiconv = extractor.getUrlIdConverter();
StreamUrlIdHandler uiconv = extractor.getUrlIdHandler();
streamInfo.service_id = extractor.getServiceId();
streamInfo.webpage_url = extractor.getPageUrl();
@@ -229,12 +229,27 @@ public class StreamInfo extends AbstractVideoInfo {
streamInfo.addException(e);
}
try {
streamInfo.next_video = extractor.getNextVideo();
} catch(Exception e) {
// get next video
if(streamInfo.next_video != null)
{
StreamPreviewInfoCollector c = new StreamPreviewInfoCollector(
extractor.getUrlIdHandler(), extractor.getServiceId());
StreamPreviewInfoExtractor nextVideo = extractor.getNextVideo();
c.commit(nextVideo);
if(c.getItemList().size() != 0) {
streamInfo.next_video = c.getItemList().get(0);
}
streamInfo.errors.addAll(c.getErrors());
}
}
catch(Exception e) {
streamInfo.addException(e);
}
try {
streamInfo.related_videos = extractor.getRelatedVideos();
// get related videos
StreamPreviewInfoCollector c = extractor.getRelatedVideos();
streamInfo.related_videos = c.getItemList();
streamInfo.errors.addAll(c.getErrors());
} catch(Exception e) {
streamInfo.addException(e);
}

View File

@@ -2,6 +2,9 @@ package org.schabi.newpipe.extractor;
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamUrlIdHandler;
import java.util.List;
import java.util.Vector;
/**
* Created by Christian Schabesberger on 28.02.16.
*
@@ -23,7 +26,8 @@ import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamUrlIdHandler;
*/
public class StreamPreviewInfoCollector {
private SearchResult result = new SearchResult();
private List<StreamPreviewInfo> itemList = new Vector<>();
private List<Exception> errors = new Vector<>();
private StreamUrlIdHandler urlIdHandler = null;
private int serviceId = -1;
@@ -32,16 +36,16 @@ public class StreamPreviewInfoCollector {
this.serviceId = serviceId;
}
public void setSuggestion(String suggestion) {
result.suggestion = suggestion;
public List<StreamPreviewInfo> getItemList() {
return itemList;
}
public List<Exception> getErrors() {
return errors;
}
public void addError(Exception e) {
result.errors.add(e);
}
public SearchResult getSearchResult() {
return result;
errors.add(e);
}
public void commit(StreamPreviewInfoExtractor extractor) throws ParsingException {
@@ -52,7 +56,7 @@ public class StreamPreviewInfoCollector {
resultItem.webpage_url = extractor.getWebPageUrl();
if (urlIdHandler == null) {
throw new ParsingException("Error: UrlIdHandler not set");
} else {
} else if(!resultItem.webpage_url.isEmpty()) {
resultItem.id = (new YoutubeStreamUrlIdHandler()).getVideoId(resultItem.webpage_url);
}
resultItem.title = extractor.getTitle();
@@ -84,11 +88,9 @@ public class StreamPreviewInfoCollector {
} catch (Exception e) {
addError(e);
}
result.resultList.add(resultItem);
itemList.add(resultItem);
} catch (Exception e) {
addError(e);
}
}
}

View File

@@ -0,0 +1,42 @@
package org.schabi.newpipe.extractor;
/**
* Created by Christian Schabesberger on 11.05.16.
*
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
* StreamPreviewInfoSearchCollector.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class StreamPreviewInfoSearchCollector extends StreamPreviewInfoCollector {
private String suggestion = "";
public StreamPreviewInfoSearchCollector(StreamUrlIdHandler handler, int serviceId) {
super(handler, serviceId);
}
public void setSuggestion(String suggestion) {
this.suggestion = suggestion;
}
public SearchResult getSearchResult() {
SearchResult result = new SearchResult();
result.suggestion = suggestion;
result.errors = getErrors();
result.resultList = getItemList();
return result;
}
}

View File

@@ -11,6 +11,7 @@ import org.schabi.newpipe.extractor.ParsingException;
import org.schabi.newpipe.extractor.SearchEngine;
import org.schabi.newpipe.extractor.StreamPreviewInfoCollector;
import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor;
import org.schabi.newpipe.extractor.StreamPreviewInfoSearchCollector;
import org.schabi.newpipe.extractor.StreamUrlIdHandler;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@@ -55,9 +56,9 @@ public class YoutubeSearchEngine extends SearchEngine {
}
@Override
public StreamPreviewInfoCollector search(String query, int page, String languageCode, Downloader downloader)
public StreamPreviewInfoSearchCollector search(String query, int page, String languageCode, Downloader downloader)
throws IOException, ExtractionException {
StreamPreviewInfoCollector collector = getStreamPreviewInfoCollector();
StreamPreviewInfoSearchCollector collector = getStreamPreviewInfoSearchCollector();
/* Cant use Uri.Bilder since it's android code.
// Android code is baned from the extractor side.

View File

@@ -47,7 +47,7 @@ public class YoutubeService extends StreamingService {
throws ExtractionException, IOException {
StreamUrlIdHandler urlIdHandler = new YoutubeStreamUrlIdHandler();
if(urlIdHandler.acceptUrl(url)) {
return new YoutubeStreamExtractor(url, downloader, getServiceId());
return new YoutubeStreamExtractor(urlIdHandler, url, downloader, getServiceId());
}
else {
throw new IllegalArgumentException("supplied String is not a valid Youtube URL");

View File

@@ -8,6 +8,7 @@ import org.jsoup.nodes.Element;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.ScriptableObject;
import org.schabi.newpipe.extractor.AbstractVideoInfo;
import org.schabi.newpipe.extractor.AudioStream;
import org.schabi.newpipe.extractor.ExtractionException;
import org.schabi.newpipe.extractor.Downloader;
@@ -15,6 +16,8 @@ import org.schabi.newpipe.extractor.Parser;
import org.schabi.newpipe.extractor.ParsingException;
import org.schabi.newpipe.extractor.StreamInfo;
import org.schabi.newpipe.extractor.StreamPreviewInfo;
import org.schabi.newpipe.extractor.StreamPreviewInfoCollector;
import org.schabi.newpipe.extractor.StreamPreviewInfoExtractor;
import org.schabi.newpipe.extractor.StreamUrlIdHandler;
import org.schabi.newpipe.extractor.StreamExtractor;
import org.schabi.newpipe.extractor.MediaFormat;
@@ -181,9 +184,10 @@ public class YoutubeStreamExtractor extends StreamExtractor {
private Downloader downloader;
public YoutubeStreamExtractor(String pageUrl, Downloader dl, int serviceId)
public YoutubeStreamExtractor(StreamUrlIdHandler urlIdHandler, String pageUrl,
Downloader dl, int serviceId)
throws ExtractionException, IOException {
super(pageUrl, dl, serviceId);
super(urlIdHandler ,pageUrl, dl, serviceId);
//most common videoInfo fields are now set in our superclass, for all services
downloader = dl;
this.pageUrl = pageUrl;
@@ -648,7 +652,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
}
@Override
public StreamPreviewInfo getNextVideo() throws ParsingException {
public StreamPreviewInfoExtractor getNextVideo() throws ParsingException {
try {
return extractVideoPreviewInfo(doc.select("div[class=\"watch-sidebar-section\"]").first()
.select("li").first());
@@ -658,26 +662,21 @@ public class YoutubeStreamExtractor extends StreamExtractor {
}
@Override
public Vector<StreamPreviewInfo> getRelatedVideos() throws ParsingException {
public StreamPreviewInfoCollector getRelatedVideos() throws ParsingException {
try {
Vector<StreamPreviewInfo> relatedVideos = new Vector<>();
StreamPreviewInfoCollector collector = getStreamPreviewInfoCollector();
for (Element li : doc.select("ul[id=\"watch-related\"]").first().children()) {
// first check if we have a playlist. If so leave them out
if (li.select("a[class*=\"content-link\"]").first() != null) {
relatedVideos.add(extractVideoPreviewInfo(li));
collector.commit(extractVideoPreviewInfo(li));
}
}
return relatedVideos;
return collector;
} catch(Exception e) {
throw new ParsingException("Could not get related videos", e);
}
}
@Override
public StreamUrlIdHandler getUrlIdConverter() {
return new YoutubeStreamUrlIdHandler();
}
@Override
public String getPageUrl() {
return pageUrl;
@@ -692,54 +691,78 @@ public class YoutubeStreamExtractor extends StreamExtractor {
/**Provides information about links to other videos on the video page, such as related videos.
* This is encapsulated in a StreamPreviewInfo object,
* which is a subset of the fields in a full StreamInfo.*/
private StreamPreviewInfo extractVideoPreviewInfo(Element li) throws ParsingException {
StreamPreviewInfo info = new StreamPreviewInfo();
try {
info.webpage_url = li.select("a.content-link").first()
.attr("abs:href");
info.id = Parser.matchGroup1("v=([0-9a-zA-Z-]*)", info.webpage_url);
//todo: check NullPointerException causing
info.title = li.select("span.title").first().text();
//this page causes the NullPointerException, after finding it by searching for "tjvg":
//https://www.youtube.com/watch?v=Uqg0aEhLFAg
//this line is unused
//String views = li.select("span.view-count").first().text();
//Log.i(TAG, "title:"+info.title);
//Log.i(TAG, "view count:"+views);
try {
info.view_count = Long.parseLong(li.select("span.view-count")
.first().text().replaceAll("[^\\d]", ""));
} catch (Exception e) {//related videos sometimes have no view count
info.view_count = 0;
private StreamPreviewInfoExtractor extractVideoPreviewInfo(final Element li) {
return new StreamPreviewInfoExtractor() {
@Override
public AbstractVideoInfo.StreamType getStreamType() throws ParsingException {
return null;
}
info.uploader = li.select("span.g-hovercard").first().text();
info.duration = YoutubeParsingHelper.parseDurationString(
li.select("span.video-time").first().text());
@Override
public String getWebPageUrl() throws ParsingException {
return li.select("a.content-link").first().attr("abs:href");
}
Element img = li.select("img").first();
info.thumbnail_url = img.attr("abs:src");
// 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.
if (info.thumbnail_url.contains(".gif")) {
info.thumbnail_url = img.attr("data-thumb");
@Override
public String getTitle() throws ParsingException {
//todo: check NullPointerException causing
return li.select("span.title").first().text();
//this page causes the NullPointerException, after finding it by searching for "tjvg":
//https://www.youtube.com/watch?v=Uqg0aEhLFAg
}
if (info.thumbnail_url.startsWith("//")) {
info.thumbnail_url = "https:" + info.thumbnail_url;
@Override
public int getDuration() throws ParsingException {
return YoutubeParsingHelper.parseDurationString(
li.select("span.video-time").first().text());
}
} catch (Exception e) {
throw new ParsingException("Could not get video preview info", e);
}
return info;
@Override
public String getUploader() throws ParsingException {
return li.select("span.g-hovercard").first().text();
}
@Override
public String getUploadDate() throws ParsingException {
return null;
}
@Override
public long getViewCount() throws ParsingException {
//this line is unused
//String views = li.select("span.view-count").first().text();
//Log.i(TAG, "title:"+info.title);
//Log.i(TAG, "view count:"+views);
try {
return Long.parseLong(li.select("span.view-count")
.first().text().replaceAll("[^\\d]", ""));
} catch (Exception e) {
//related videos sometimes have no view count
return 0;
}
}
@Override
public String getThumbnailUrl() throws ParsingException {
Element img = li.select("img").first();
String thumbnailUrl = img.attr("abs:src");
// 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.
if (thumbnailUrl.contains(".gif")) {
thumbnailUrl = img.attr("data-thumb");
}
if (thumbnailUrl.startsWith("//")) {
thumbnailUrl = "https:" + thumbnailUrl;
}
return thumbnailUrl;
}
};
}
private String loadDecryptionCode(String playerUrl) throws DecryptException {
String decryptionFuncName;
String decryptionFunc;
@@ -756,7 +779,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
String functionPattern = "("
+ decryptionFuncName.replace("$", "\\$")
+ "=function\\([a-zA-Z0-9_]*\\)\\{.+?\\})";
+ "=function\\([a-zA-Z0-9_]+\\)\\{.+?\\})";
decryptionFunc = "var " + Parser.matchGroup1(functionPattern, playerCode) + ";";
helperObjectName = Parser

View File

@@ -36,7 +36,11 @@ public class YoutubeStreamUrlIdHandler implements StreamUrlIdHandler {
@SuppressWarnings("WeakerAccess")
@Override
public String getVideoId(String url) throws ParsingException {
public String getVideoId(String url) throws ParsingException, IllegalArgumentException {
if(url.isEmpty())
{
throw new IllegalArgumentException("The url parameter should not be empty");
}
String id;
if(url.contains("youtube")) {
@@ -48,7 +52,12 @@ public class YoutubeStreamUrlIdHandler implements StreamUrlIdHandler {
} catch(UnsupportedEncodingException uee) {
throw new ParsingException("Could not parse attribution_link", uee);
}
} else {
}
else if(url.contains("vnd.youtube"))
{
id = Parser.matchGroup1("vnd.youtube\\:([\\-a-zA-Z0-9_]{11}).*", url);
}
else {
id = Parser.matchGroup1("[?&]v=([\\-a-zA-Z0-9_]{11})", url);
}
}

View File

@@ -0,0 +1,14 @@
package us.shandian.giga.get;
public interface DownloadManager
{
public static final int BLOCK_SIZE = 512 * 1024;
public int startMission(String url, String name, int threads);
public void resumeMission(int id);
public void pauseMission(int id);
public void deleteMission(int id);
public DownloadMission getMission(int id);
public int getCount();
public String getLocation();
}

View File

@@ -0,0 +1,216 @@
package us.shandian.giga.get;
import android.content.Context;
import android.util.Log;
import com.google.gson.Gson;
import org.schabi.newpipe.NewPipeSettings;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import us.shandian.giga.util.Utility;
import static org.schabi.newpipe.BuildConfig.DEBUG;
public class DownloadManagerImpl implements DownloadManager
{
private static final String TAG = DownloadManagerImpl.class.getSimpleName();
private Context mContext;
private String mLocation;
protected ArrayList<DownloadMission> mMissions = new ArrayList<DownloadMission>();
public DownloadManagerImpl(Context context, String location) {
mContext = context;
mLocation = location;
loadMissions();
}
@Override
public int startMission(String url, String name, int threads) {
DownloadMission mission = new DownloadMission();
mission.url = url;
mission.name = name;
mission.location = NewPipeSettings.getDownloadPath(mContext, name);
mission.timestamp = System.currentTimeMillis();
mission.threadCount = threads;
new Initializer(mContext, mission).start();
return insertMission(mission);
}
@Override
public void resumeMission(int i) {
DownloadMission d = getMission(i);
if (!d.running && d.errCode == -1) {
d.start();
}
}
@Override
public void pauseMission(int i) {
DownloadMission d = getMission(i);
if (d.running) {
d.pause();
}
}
@Override
public void deleteMission(int i) {
getMission(i).delete();
mMissions.remove(i);
}
private void loadMissions() {
File f = new File(mLocation);
if (f.exists() && f.isDirectory()) {
File[] subs = f.listFiles();
for (File sub : subs) {
if (sub.isDirectory()) {
continue;
}
if (sub.getName().endsWith(".giga")) {
String str = Utility.readFromFile(sub.getAbsolutePath());
if (str != null && !str.trim().equals("")) {
if (DEBUG) {
Log.d(TAG, "loading mission " + sub.getName());
Log.d(TAG, str);
}
DownloadMission mis = new Gson().fromJson(str, DownloadMission.class);
if (mis.finished) {
sub.delete();
continue;
}
mis.running = false;
mis.recovered = true;
insertMission(mis);
}
} else if (!sub.getName().startsWith(".") && !new File(sub.getPath() + ".giga").exists()) {
// Add a dummy mission for downloaded files
DownloadMission mis = new DownloadMission();
mis.length = sub.length();
mis.done = mis.length;
mis.finished = true;
mis.running = false;
mis.name = sub.getName();
mis.location = mLocation;
mis.timestamp = sub.lastModified();
insertMission(mis);
}
}
}
}
@Override
public DownloadMission getMission(int i) {
return mMissions.get(i);
}
@Override
public int getCount() {
return mMissions.size();
}
private int insertMission(DownloadMission mission) {
int i = -1;
DownloadMission m = null;
if (mMissions.size() > 0) {
do {
m = mMissions.get(++i);
} while (m.timestamp > mission.timestamp && i < mMissions.size() - 1);
//if (i > 0) i--;
} else {
i = 0;
}
mMissions.add(i, mission);
return i;
}
@Override
public String getLocation() {
return mLocation;
}
private class Initializer extends Thread {
private Context context;
private DownloadMission mission;
public Initializer(Context context, DownloadMission mission) {
this.context = context;
this.mission = mission;
}
@Override
public void run() {
try {
URL url = new URL(mission.url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
mission.length = conn.getContentLength();
if (mission.length <= 0) {
mission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
//mission.notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
return;
}
// Open again
conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Range", "bytes=" + (mission.length - 10) + "-" + mission.length);
if (conn.getResponseCode() != 206) {
// Fallback to single thread if no partial content support
mission.fallback = true;
if (DEBUG) {
Log.d(TAG, "falling back");
}
}
if (DEBUG) {
Log.d(TAG, "response = " + conn.getResponseCode());
}
mission.blocks = mission.length / BLOCK_SIZE;
if (mission.threadCount > mission.blocks) {
mission.threadCount = (int) mission.blocks;
}
if (mission.threadCount <= 0) {
mission.threadCount = 1;
}
if (mission.blocks * BLOCK_SIZE < mission.length) {
mission.blocks++;
}
new File(mission.location).mkdirs();
new File(mission.location + "/" + mission.name).createNewFile();
RandomAccessFile af = new RandomAccessFile(mission.location + "/" + mission.name, "rw");
af.setLength(mission.length);
af.close();
mission.start();
} catch (Exception e) {
// TODO Notify
throw new RuntimeException(e);
}
}
}
}

View File

@@ -0,0 +1,229 @@
package us.shandian.giga.get;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.google.gson.Gson;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.HashMap;
import us.shandian.giga.util.Utility;
import static org.schabi.newpipe.BuildConfig.DEBUG;
public class DownloadMission
{
private static final String TAG = DownloadMission.class.getSimpleName();
public static interface MissionListener {
HashMap<MissionListener, Handler> handlerStore = new HashMap<>();
public void onProgressUpdate(long done, long total);
public void onFinish();
public void onError(int errCode);
}
public static final int ERROR_SERVER_UNSUPPORTED = 206;
public static final int ERROR_UNKNOWN = 233;
public String name = "";
public String url = "";
public String location = "";
public long blocks = 0;
public long length = 0;
public long done = 0;
public int threadCount = 3;
public int finishCount = 0;
public ArrayList<Long> threadPositions = new ArrayList<Long>();
public HashMap<Long, Boolean> blockState = new HashMap<Long, Boolean>();
public boolean running = false;
public boolean finished = false;
public boolean fallback = false;
public int errCode = -1;
public long timestamp = 0;
public transient boolean recovered = false;
private transient ArrayList<WeakReference<MissionListener>> mListeners = new ArrayList<WeakReference<MissionListener>>();
private transient boolean mWritingToFile = false;
public boolean isBlockPreserved(long block) {
return blockState.containsKey(block) ? blockState.get(block) : false;
}
public void preserveBlock(long block) {
synchronized (blockState) {
blockState.put(block, true);
}
}
public void setPosition(int id, long position) {
threadPositions.set(id, position);
}
public long getPosition(int id) {
return threadPositions.get(id);
}
public synchronized void notifyProgress(long deltaLen) {
if (!running) return;
if (recovered) {
recovered = false;
}
done += deltaLen;
if (done > length) {
done = length;
}
if (done != length) {
writeThisToFile();
}
for (WeakReference<MissionListener> ref: mListeners) {
final MissionListener listener = ref.get();
if (listener != null) {
MissionListener.handlerStore.get(listener).post(new Runnable() {
@Override
public void run() {
listener.onProgressUpdate(done, length);
}
});
}
}
}
public synchronized void notifyFinished() {
if (errCode > 0) return;
finishCount++;
if (finishCount == threadCount) {
onFinish();
}
}
private void onFinish() {
if (errCode > 0) return;
if (DEBUG) {
Log.d(TAG, "onFinish");
}
running = false;
finished = true;
deleteThisFromFile();
for (WeakReference<MissionListener> ref : mListeners) {
final MissionListener listener = ref.get();
if (listener != null) {
MissionListener.handlerStore.get(listener).post(new Runnable() {
@Override
public void run() {
listener.onFinish();
}
});
}
}
}
public synchronized void notifyError(int err) {
errCode = err;
writeThisToFile();
for (WeakReference<MissionListener> ref : mListeners) {
final MissionListener listener = ref.get();
MissionListener.handlerStore.get(listener).post(new Runnable() {
@Override
public void run() {
listener.onError(errCode);
}
});
}
}
public synchronized void addListener(MissionListener listener) {
Handler handler = new Handler(Looper.getMainLooper());
MissionListener.handlerStore.put(listener, handler);
mListeners.add(new WeakReference<MissionListener>(listener));
}
public synchronized void removeListener(MissionListener listener) {
for (Iterator<WeakReference<MissionListener>> iterator = mListeners.iterator();
iterator.hasNext(); ) {
WeakReference<MissionListener> weakRef = iterator.next();
if (listener!=null && listener == weakRef.get())
{
iterator.remove();
}
}
}
public void start() {
if (!running && !finished) {
running = true;
if (!fallback) {
for (int i = 0; i < threadCount; i++) {
if (threadPositions.size() <= i && !recovered) {
threadPositions.add((long) i);
}
new Thread(new DownloadRunnable(this, i)).start();
}
} else {
// In fallback mode, resuming is not supported.
threadCount = 1;
done = 0;
blocks = 0;
new Thread(new DownloadRunnableFallback(this)).start();
}
}
}
public void pause() {
if (running) {
running = false;
recovered = true;
// TODO: Notify & Write state to info file
// if (err)
}
}
public void delete() {
deleteThisFromFile();
new File(location + "/" + name).delete();
}
public void writeThisToFile() {
if (!mWritingToFile) {
mWritingToFile = true;
new Thread() {
@Override
public void run() {
doWriteThisToFile();
mWritingToFile = false;
}
}.start();
}
}
private void doWriteThisToFile() {
synchronized (blockState) {
Utility.writeToFile(location + "/" + name + ".giga", new Gson().toJson(this));
}
}
private void deleteThisFromFile() {
new File(location + "/" + name + ".giga").delete();
}
}

View File

@@ -0,0 +1,173 @@
package us.shandian.giga.get;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import static org.schabi.newpipe.BuildConfig.DEBUG;
public class DownloadRunnable implements Runnable
{
private static final String TAG = DownloadRunnable.class.getSimpleName();
private DownloadMission mMission;
private int mId;
public DownloadRunnable(DownloadMission mission, int id) {
mMission = mission;
mId = id;
}
@Override
public void run() {
boolean retry = mMission.recovered;
long position = mMission.getPosition(mId);
if (DEBUG) {
Log.d(TAG, mId + ":default pos " + position);
Log.d(TAG, mId + ":recovered: " + mMission.recovered);
}
while (mMission.errCode == -1 && mMission.running && position < mMission.blocks) {
if (Thread.currentThread().isInterrupted()) {
mMission.pause();
return;
}
if (DEBUG && retry) {
Log.d(TAG, mId + ":retry is true. Resuming at " + position);
}
// Wait for an unblocked position
while (!retry && position < mMission.blocks && mMission.isBlockPreserved(position)) {
if (DEBUG) {
Log.d(TAG, mId + ":position " + position + " preserved, passing");
}
position++;
}
retry = false;
if (position >= mMission.blocks) {
break;
}
if (DEBUG) {
Log.d(TAG, mId + ":preserving position " + position);
}
mMission.preserveBlock(position);
mMission.setPosition(mId, position);
long start = position * DownloadManager.BLOCK_SIZE;
long end = start + DownloadManager.BLOCK_SIZE - 1;
if (end >= mMission.length) {
end = mMission.length - 1;
}
HttpURLConnection conn = null;
int total = 0;
try {
URL url = new URL(mMission.url);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
if (DEBUG) {
Log.d(TAG, mId + ":" + conn.getRequestProperty("Range"));
Log.d(TAG, mId + ":Content-Length=" + conn.getContentLength() + " Code:" + conn.getResponseCode());
}
// A server may be ignoring the range requet
if (conn.getResponseCode() != 206) {
mMission.errCode = DownloadMission.ERROR_SERVER_UNSUPPORTED;
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
if (DEBUG) {
Log.e(TAG, mId + ":Unsupported " + conn.getResponseCode());
}
break;
}
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
f.seek(start);
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
byte[] buf = new byte[512];
while (start < end && mMission.running) {
int len = ipt.read(buf, 0, 512);
if (len == -1) {
break;
} else {
start += len;
total += len;
f.write(buf, 0, len);
notifyProgress(len);
}
}
if (DEBUG && mMission.running) {
Log.d(TAG, mId + ":position " + position + " finished, total length " + total);
}
f.close();
ipt.close();
// TODO We should save progress for each thread
} catch (Exception e) {
// TODO Retry count limit & notify error
retry = true;
notifyProgress(-total);
if (DEBUG) {
Log.d(TAG, mId + ":position " + position + " retrying");
}
}
}
if (DEBUG) {
Log.d(TAG, "thread " + mId + " exited main loop");
}
if (mMission.errCode == -1 && mMission.running) {
if (DEBUG) {
Log.d(TAG, "no error has happened, notifying");
}
notifyFinished();
}
if (DEBUG && !mMission.running) {
Log.d(TAG, "The mission has been paused. Passing.");
}
}
private void notifyProgress(final long len) {
synchronized (mMission) {
mMission.notifyProgress(len);
}
}
private void notifyError(final int err) {
synchronized (mMission) {
mMission.notifyError(err);
mMission.pause();
}
}
private void notifyFinished() {
synchronized (mMission) {
mMission.notifyFinished();
}
}
}

View File

@@ -0,0 +1,74 @@
package us.shandian.giga.get;
import java.io.BufferedInputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
// Single-threaded fallback mode
public class DownloadRunnableFallback implements Runnable
{
private DownloadMission mMission;
//private int mId;
public DownloadRunnableFallback(DownloadMission mission) {
//mId = id;
mMission = mission;
}
@Override
public void run() {
try {
URL url = new URL(mMission.url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn.getResponseCode() != 200 && conn.getResponseCode() != 206) {
notifyError(DownloadMission.ERROR_SERVER_UNSUPPORTED);
} else {
RandomAccessFile f = new RandomAccessFile(mMission.location + "/" + mMission.name, "rw");
f.seek(0);
BufferedInputStream ipt = new BufferedInputStream(conn.getInputStream());
byte[] buf = new byte[512];
int len = 0;
while ((len = ipt.read(buf, 0, 512)) != -1 && mMission.running) {
f.write(buf, 0, len);
notifyProgress(len);
if (Thread.currentThread().interrupted()) {
break;
}
}
f.close();
ipt.close();
}
} catch (Exception e) {
notifyError(DownloadMission.ERROR_UNKNOWN);
}
if (mMission.errCode == -1 && mMission.running) {
notifyFinished();
}
}
private void notifyProgress(final long len) {
synchronized (mMission) {
mMission.notifyProgress(len);
}
}
private void notifyError(final int err) {
synchronized (mMission) {
mMission.notifyError(err);
mMission.pause();
}
}
private void notifyFinished() {
synchronized (mMission) {
mMission.notifyFinished();
}
}
}

View File

@@ -0,0 +1,186 @@
package us.shandian.giga.service;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.support.v4.app.NotificationCompat.Builder;
import android.util.Log;
import org.schabi.newpipe.NewPipeSettings;
import org.schabi.newpipe.R;
import us.shandian.giga.get.DownloadManager;
import us.shandian.giga.get.DownloadManagerImpl;
import us.shandian.giga.get.DownloadMission;
import org.schabi.newpipe.download.MainActivity;
import static org.schabi.newpipe.BuildConfig.DEBUG;
public class DownloadManagerService extends Service implements DownloadMission.MissionListener
{
private static final String TAG = DownloadManagerService.class.getSimpleName();
private DMBinder mBinder;
private DownloadManager mManager;
private Notification mNotification;
private Handler mHandler;
private long mLastTimeStamp = System.currentTimeMillis();
@Override
public void onCreate() {
super.onCreate();
if (DEBUG) {
Log.d(TAG, "onCreate");
}
mBinder = new DMBinder();
if (mManager == null) {
String path = NewPipeSettings.getVideoDownloadPath(this);
mManager = new DownloadManagerImpl(this, path);
if (DEBUG) {
Log.d(TAG, "mManager == null");
Log.d(TAG, "Download directory: " + path);
}
}
Intent i = new Intent();
i.setAction(Intent.ACTION_MAIN);
i.setClass(this, MainActivity.class);
Drawable icon = this.getResources().getDrawable(R.mipmap.ic_launcher);
Builder builder = new Builder(this)
.setContentIntent(PendingIntent.getActivity(this, 0, i, 0))
.setSmallIcon(android.R.drawable.stat_sys_download)
.setLargeIcon(((BitmapDrawable) icon).getBitmap())
.setContentTitle(getString(R.string.msg_running))
.setContentText(getString(R.string.msg_running_detail));
PendingIntent pendingIntent =
PendingIntent.getActivity(
this,
0,
new Intent(this, MainActivity.class)
.setAction(MainActivity.INTENT_LIST),
PendingIntent.FLAG_UPDATE_CURRENT
);
builder.setContentIntent(pendingIntent);
mNotification = builder.build();
HandlerThread thread = new HandlerThread("ServiceMessenger");
thread.start();
mHandler = new Handler(thread.getLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == 0) {
int runningCount = 0;
for (int i = 0; i < mManager.getCount(); i++) {
if (mManager.getMission(i).running) {
runningCount++;
}
}
updateState(runningCount);
}
}
};
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (DEBUG) {
Log.d(TAG, "Starting");
}
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
if (DEBUG) {
Log.d(TAG, "Destroying");
}
for (int i = 0; i < mManager.getCount(); i++) {
mManager.pauseMission(i);
}
stopForeground(true);
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onProgressUpdate(long done, long total) {
long now = System.currentTimeMillis();
long delta = now - mLastTimeStamp;
if (delta > 2000) {
postUpdateMessage();
mLastTimeStamp = now;
}
}
@Override
public void onFinish() {
postUpdateMessage();
}
@Override
public void onError(int errCode) {
postUpdateMessage();
}
private void postUpdateMessage() {
mHandler.sendEmptyMessage(0);
}
private void updateState(int runningCount) {
if (runningCount == 0) {
stopForeground(true);
} else {
startForeground(1000, mNotification);
}
}
// Wrapper of DownloadManager
public class DMBinder extends Binder {
public DownloadManager getDownloadManager() {
return mManager;
}
public void onMissionAdded(DownloadMission mission) {
mission.addListener(DownloadManagerService.this);
postUpdateMessage();
}
public void onMissionRemoved(DownloadMission mission) {
mission.removeListener(DownloadManagerService.this);
postUpdateMessage();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
package us.shandian.giga.ui.common;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
public class ProgressDrawable extends Drawable
{
private float mProgress = 0.0f;
private int mBackgroundColor, mForegroundColor;
public ProgressDrawable(Context context, int background, int foreground) {
this(context.getResources().getColor(background), context.getResources().getColor(foreground));
}
public ProgressDrawable(int background, int foreground) {
mBackgroundColor = background;
mForegroundColor = foreground;
}
public void setProgress(float progress) {
mProgress = progress;
invalidateSelf();
}
@Override
public void draw(Canvas canvas) {
int width = canvas.getWidth();
int height = canvas.getHeight();
Paint paint = new Paint();
paint.setColor(mBackgroundColor);
canvas.drawRect(0, 0, width, height, paint);
paint.setColor(mForegroundColor);
canvas.drawRect(0, 0, (int) (mProgress * width), height, paint);
}
@Override
public void setAlpha(int alpha) {
// Unsupported
}
@Override
public void setColorFilter(ColorFilter filter) {
// Unsupported
}
@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
}

Some files were not shown because too many files have changed in this diff Show More