mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-09-24 08:40:51 +02:00
Compare commits
460 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
140cfaec90 | ||
![]() |
a0f20aac23 | ||
![]() |
93fafb362f | ||
![]() |
9152df5512 | ||
![]() |
415bcd9f7e | ||
![]() |
2224033a85 | ||
![]() |
ade0498684 | ||
![]() |
211b00fff4 | ||
![]() |
77c72d03a8 | ||
![]() |
f429d93351 | ||
![]() |
e428f8b116 | ||
![]() |
9482b9d638 | ||
![]() |
e019c9f720 | ||
![]() |
e2778366e9 | ||
![]() |
36d2f8339c | ||
![]() |
a4a0c3b9fd | ||
![]() |
1b1b6c8af8 | ||
![]() |
064fd2bc68 | ||
![]() |
13152ab6ea | ||
![]() |
1743d821eb | ||
![]() |
ff16e577ef | ||
![]() |
86062c6e94 | ||
![]() |
8ce2350563 | ||
![]() |
cb8989af7f | ||
![]() |
877fa45eb4 | ||
![]() |
e33942486d | ||
![]() |
9606e080ef | ||
![]() |
d5cd9c55be | ||
![]() |
9c197ced80 | ||
![]() |
f8424599e1 | ||
![]() |
ccee18057a | ||
![]() |
1ad0f342ad | ||
![]() |
7a16ac574b | ||
![]() |
f46738f750 | ||
![]() |
fb8ff3fece | ||
![]() |
7498dd3800 | ||
![]() |
957c31b9c5 | ||
![]() |
b260570e8c | ||
![]() |
46542747b7 | ||
![]() |
d64480fc9b | ||
![]() |
c00e694d40 | ||
![]() |
f0761cc95e | ||
![]() |
068554955c | ||
![]() |
a2f915f556 | ||
![]() |
cffd35c974 | ||
![]() |
2ef2aa98a4 | ||
![]() |
ba98b30aa4 | ||
![]() |
87c0f9c803 | ||
![]() |
ce620b9e28 | ||
![]() |
fcbb5536eb | ||
![]() |
72df6dbd80 | ||
![]() |
3a6b023dbf | ||
![]() |
8b67f1358d | ||
![]() |
2aebb3b8db | ||
![]() |
f2b38be2b0 | ||
![]() |
a43001f30d | ||
![]() |
17abe1ea5c | ||
![]() |
6aaefab618 | ||
![]() |
cd867fb4d1 | ||
![]() |
25988f61a6 | ||
![]() |
be421e580d | ||
![]() |
641ab25470 | ||
![]() |
a508539c2e | ||
![]() |
cac79d9a0d | ||
![]() |
9ede8118da | ||
![]() |
e47761750a | ||
![]() |
2f181ce7c9 | ||
![]() |
3a13d4a1de | ||
![]() |
719de00e0f | ||
![]() |
ddffe99f53 | ||
![]() |
9ade596f06 | ||
![]() |
46f413b7f2 | ||
![]() |
5fe2e7b8ad | ||
![]() |
3008cbb5f4 | ||
![]() |
f7983960e5 | ||
![]() |
bb8007bb7c | ||
![]() |
01751ba97a | ||
![]() |
f41475d11c | ||
![]() |
d8914d9b6d | ||
![]() |
94cc2ad365 | ||
![]() |
e07464b81c | ||
![]() |
9d231b55b5 | ||
![]() |
77d8dac3c1 | ||
![]() |
e160015283 | ||
![]() |
aeb0cac3ee | ||
![]() |
a539f94837 | ||
![]() |
df70751071 | ||
![]() |
e55f1dff78 | ||
![]() |
47646e1c62 | ||
![]() |
80e673f20c | ||
![]() |
9ca8c5480c | ||
![]() |
4d0d3c7ead | ||
![]() |
58137aadc9 | ||
![]() |
82a59ae479 | ||
![]() |
affd23b14e | ||
![]() |
9c7f249756 | ||
![]() |
e2a0502171 | ||
![]() |
3832a4b355 | ||
![]() |
84f059415c | ||
![]() |
bb292e3199 | ||
![]() |
70541bf561 | ||
![]() |
48dd8e88e3 | ||
![]() |
52c2e0899d | ||
![]() |
9955d5b62f | ||
![]() |
8e26247fa1 | ||
![]() |
72924e2692 | ||
![]() |
8c2c8d630f | ||
![]() |
5ccf0baa6b | ||
![]() |
8fff0ccdf2 | ||
![]() |
35264bfb97 | ||
![]() |
4c9b0dc8e9 | ||
![]() |
4deb7caa83 | ||
![]() |
45f1a512b4 | ||
![]() |
ab95fdc087 | ||
![]() |
df608b9ded | ||
![]() |
6a4c81d160 | ||
![]() |
040d658540 | ||
![]() |
cbc9913e9c | ||
![]() |
f007cbd5ee | ||
![]() |
7351591df7 | ||
![]() |
5222d4cbba | ||
![]() |
ef9966815e | ||
![]() |
ad73440ce9 | ||
![]() |
c81cfdc455 | ||
![]() |
134c3804db | ||
![]() |
e355df5eda | ||
![]() |
a68e0a95f4 | ||
![]() |
6fd6facf72 | ||
![]() |
00102d4048 | ||
![]() |
adbeff11d4 | ||
![]() |
a8fe329678 | ||
![]() |
a4b61bf730 | ||
![]() |
ee592def0c | ||
![]() |
c14e117239 | ||
![]() |
b6e94dde0c | ||
![]() |
63a4a44d7e | ||
![]() |
eb8ab5d527 | ||
![]() |
b778d40d65 | ||
![]() |
2f570672dc | ||
![]() |
9290ed490f | ||
![]() |
1bda812c75 | ||
![]() |
ab82406c98 | ||
![]() |
be763349df | ||
![]() |
91764ad601 | ||
![]() |
1c4031aa38 | ||
![]() |
c3bc648dd4 | ||
![]() |
c97d794272 | ||
![]() |
8e7d2e91e9 | ||
![]() |
cf0fbbbd3d | ||
![]() |
7d3ede7946 | ||
![]() |
1aa308eb5d | ||
![]() |
4bfd30e34a | ||
![]() |
0f46c90688 | ||
![]() |
aa2173fc4a | ||
![]() |
932cb1d19c | ||
![]() |
18b038d8e4 | ||
![]() |
2ac71c75c0 | ||
![]() |
4067ba5232 | ||
![]() |
ab47dd5a5b | ||
![]() |
3130910307 | ||
![]() |
22113439a4 | ||
![]() |
2d25a9ad7a | ||
![]() |
8f0a2cc2f0 | ||
![]() |
4ca39258c9 | ||
![]() |
d0c0238b8b | ||
![]() |
f9f08e5169 | ||
![]() |
cfc51b2401 | ||
![]() |
54c2704cb5 | ||
![]() |
9b51c946fe | ||
![]() |
06f38cbcb4 | ||
![]() |
f368d6b257 | ||
![]() |
f4301da14d | ||
![]() |
f34c09f165 | ||
![]() |
6d1db56512 | ||
![]() |
b70c07d004 | ||
![]() |
f9f48a5eb6 | ||
![]() |
14e4e73444 | ||
![]() |
f2ce4d2daf | ||
![]() |
468ebdda87 | ||
![]() |
a1f0fb3b14 | ||
![]() |
3c9f4de234 | ||
![]() |
8ae411619f | ||
![]() |
61e5c9121a | ||
![]() |
8a3cf0d5dc | ||
![]() |
ecb5df65ac | ||
![]() |
5f1e98a0d3 | ||
![]() |
3b9a477499 | ||
![]() |
e5bf98a741 | ||
![]() |
06fafc247e | ||
![]() |
e8bb17b631 | ||
![]() |
363cd07883 | ||
![]() |
2b4a9286c4 | ||
![]() |
34c985026f | ||
![]() |
4a0aa42914 | ||
![]() |
746c2a15bf | ||
![]() |
9318bb5306 | ||
![]() |
2a10ceb74f | ||
![]() |
83f4db59e2 | ||
![]() |
9f66f759ad | ||
![]() |
6f015349e8 | ||
![]() |
a37802c2b9 | ||
![]() |
4fa3baf5e1 | ||
![]() |
ef8c2c81d5 | ||
![]() |
8c80d8c457 | ||
![]() |
1596872c54 | ||
![]() |
da8873fa78 | ||
![]() |
ecd8439b3f | ||
![]() |
5d178532ac | ||
![]() |
08e40a013d | ||
![]() |
c136f7363c | ||
![]() |
a60f10d739 | ||
![]() |
ae46afcb42 | ||
![]() |
bffb9f6800 | ||
![]() |
79aa9ad04b | ||
![]() |
ff5714f04a | ||
![]() |
ce499a9766 | ||
![]() |
d3bb8b7651 | ||
![]() |
6d9c23c4cb | ||
![]() |
b95a9332a9 | ||
![]() |
609715eb5c | ||
![]() |
a1266c919c | ||
![]() |
a1925a0302 | ||
![]() |
a7a4c03372 | ||
![]() |
37201600e0 | ||
![]() |
a94f40ed62 | ||
![]() |
0b08cf8c76 | ||
![]() |
33e29be7db | ||
![]() |
bd1c7851c7 | ||
![]() |
82faea5965 | ||
![]() |
0bbbfd3217 | ||
![]() |
fb578ecda8 | ||
![]() |
e804647a65 | ||
![]() |
c3c3a94593 | ||
![]() |
9d55569f80 | ||
![]() |
5dd8271c15 | ||
![]() |
7a4a54c3ea | ||
![]() |
7c9078a625 | ||
![]() |
71ae342f52 | ||
![]() |
b43c56085d | ||
![]() |
83d2ab95e0 | ||
![]() |
20a8d7372c | ||
![]() |
c36ba88db7 | ||
![]() |
4aa23023ee | ||
![]() |
8735cf931a | ||
![]() |
2473069326 | ||
![]() |
03bab57a97 | ||
![]() |
e3baf69533 | ||
![]() |
461f747af1 | ||
![]() |
028872a7d8 | ||
![]() |
a1483b6c55 | ||
![]() |
e406ba094c | ||
![]() |
c100d15ba8 | ||
![]() |
b4ea592638 | ||
![]() |
16b757d9a3 | ||
![]() |
2aa801a392 | ||
![]() |
b838344526 | ||
![]() |
233a3df222 | ||
![]() |
da6661b1ea | ||
![]() |
c6e120fc51 | ||
![]() |
c416a1254d | ||
![]() |
2aa4f6ddda | ||
![]() |
129597023d | ||
![]() |
02aed86b7e | ||
![]() |
e44f4b5823 | ||
![]() |
2f0c0f0fc2 | ||
![]() |
e5ce3f3007 | ||
![]() |
095a2be748 | ||
![]() |
c75fe88757 | ||
![]() |
a8ff4b0744 | ||
![]() |
c02c511e31 | ||
![]() |
b9550fb528 | ||
![]() |
a37d8f083a | ||
![]() |
abff1f537b | ||
![]() |
483fde5e42 | ||
![]() |
761b7ea57b | ||
![]() |
af92631a0c | ||
![]() |
ffe832d061 | ||
![]() |
a08cbfcef1 | ||
![]() |
d2d6ac1bf8 | ||
![]() |
615ffca64b | ||
![]() |
2fcf6197c5 | ||
![]() |
22d31ae14f | ||
![]() |
8c786e121b | ||
![]() |
99d2a33c8c | ||
![]() |
010a24db3f | ||
![]() |
9408da453b | ||
![]() |
dbda4202fd | ||
![]() |
5776e91459 | ||
![]() |
664d7b69b1 | ||
![]() |
6b9140c60d | ||
![]() |
383857d110 | ||
![]() |
cffd049c8a | ||
![]() |
ecabc65e94 | ||
![]() |
780f1d5da3 | ||
![]() |
e2d0fc34a3 | ||
![]() |
3404231457 | ||
![]() |
b17928570d | ||
![]() |
8ed86261ef | ||
![]() |
a313b91a0a | ||
![]() |
7b6dae20be | ||
![]() |
552c70bb0d | ||
![]() |
8f734737f0 | ||
![]() |
800e7bcb7a | ||
![]() |
2002234d86 | ||
![]() |
5923663e08 | ||
![]() |
4cdf20ab8c | ||
![]() |
af65a1cfef | ||
![]() |
927057ab83 | ||
![]() |
ffbc001ad5 | ||
![]() |
f5625a1151 | ||
![]() |
ce2ceb8a1b | ||
![]() |
c14771534f | ||
![]() |
553cec16d5 | ||
![]() |
d17496f720 | ||
![]() |
89e70626eb | ||
![]() |
2ccae841d6 | ||
![]() |
07f6d0f149 | ||
![]() |
319d769233 | ||
![]() |
6ec393699e | ||
![]() |
f8d9e0fa60 | ||
![]() |
50ed962a82 | ||
![]() |
8654705e9b | ||
![]() |
b79ed8185f | ||
![]() |
0d6c67f64f | ||
![]() |
e97a6569a6 | ||
![]() |
d0d41c6b16 | ||
![]() |
f7a531e71b | ||
![]() |
c28fddc4dd | ||
![]() |
b6ea10fc73 | ||
![]() |
320eb44061 | ||
![]() |
7a6b5dd5b7 | ||
![]() |
460653ed16 | ||
![]() |
b6fda788c5 | ||
![]() |
da4b9306fa | ||
![]() |
d76c02cbf4 | ||
![]() |
4d5466b5cd | ||
![]() |
e9c20ac8b0 | ||
![]() |
961820a250 | ||
![]() |
4cc2976061 | ||
![]() |
477f182b43 | ||
![]() |
a5252bb765 | ||
![]() |
3f0078f38a | ||
![]() |
91434dd2ac | ||
![]() |
f29bd0a6e7 | ||
![]() |
7186f58374 | ||
![]() |
5c8bcf15ba | ||
![]() |
130757ee99 | ||
![]() |
5020a4f9dc | ||
![]() |
ef15902ec4 | ||
![]() |
ed8d13f837 | ||
![]() |
c2fcae7c43 | ||
![]() |
27f2c65e6d | ||
![]() |
0b3428ede9 | ||
![]() |
3e0102ad2a | ||
![]() |
c42002ccc5 | ||
![]() |
e8101d5d91 | ||
![]() |
ecdf4ad502 | ||
![]() |
af760f9cdc | ||
![]() |
b3491da49f | ||
![]() |
c322feeaff | ||
![]() |
b26f46a063 | ||
![]() |
9f0944dc5a | ||
![]() |
e869098434 | ||
![]() |
4baa23af5f | ||
![]() |
78fc5bbbd5 | ||
![]() |
a69784d168 | ||
![]() |
d48a7f1a18 | ||
![]() |
082c6128ad | ||
![]() |
7c9771873b | ||
![]() |
7257cdacc8 | ||
![]() |
140b480f82 | ||
![]() |
77db2f8a48 | ||
![]() |
22bc81b0eb | ||
![]() |
b52b3d4be0 | ||
![]() |
f845098b42 | ||
![]() |
0f7397e3b8 | ||
![]() |
0b9d99fdc9 | ||
![]() |
9a1da5cc75 | ||
![]() |
9e76f94cf6 | ||
![]() |
7d6b92e064 | ||
![]() |
849a45a3ca | ||
![]() |
7ddea5a71b | ||
![]() |
5d81358c15 | ||
![]() |
492aad9d70 | ||
![]() |
647cfcd401 | ||
![]() |
c60d98e52d | ||
![]() |
d3cfac6b15 | ||
![]() |
7fb4e5a143 | ||
![]() |
8e451b2a83 | ||
![]() |
8083f06fe7 | ||
![]() |
44521a2e56 | ||
![]() |
dfeed3d0eb | ||
![]() |
081a45b70d | ||
![]() |
616a721bba | ||
![]() |
c9aa553b32 | ||
![]() |
e3d59c3cff | ||
![]() |
df51635674 | ||
![]() |
1e81f61760 | ||
![]() |
a4043eab83 | ||
![]() |
60dc19d2bc | ||
![]() |
a2e4585fe8 | ||
![]() |
b5df281447 | ||
![]() |
650917f9f9 | ||
![]() |
3a9c95a9ae | ||
![]() |
5458acfcad | ||
![]() |
68e80e6054 | ||
![]() |
e4aa69b8d3 | ||
![]() |
dfd40e43da | ||
![]() |
7efd111d9c | ||
![]() |
6809172203 | ||
![]() |
40a4343f06 | ||
![]() |
cd9333b39e | ||
![]() |
82d426c781 | ||
![]() |
c7e773de25 | ||
![]() |
2ded33110f | ||
![]() |
b26c0aa9da | ||
![]() |
2fc8fb6f17 | ||
![]() |
ea76f1d6e2 | ||
![]() |
7cda1d116b | ||
![]() |
8bf7a1a9db | ||
![]() |
0aa0ad65c0 | ||
![]() |
53ff58daa3 | ||
![]() |
b3225bebe6 | ||
![]() |
4beafad71f | ||
![]() |
c96f933626 | ||
![]() |
bea6359d5f | ||
![]() |
92db9cb59b | ||
![]() |
410c4ca736 | ||
![]() |
80c9dbf180 | ||
![]() |
c9edac2820 | ||
![]() |
143df9a529 | ||
![]() |
c87da9903f | ||
![]() |
0f69e6c64d | ||
![]() |
1b9a6e53ce | ||
![]() |
c7abf377eb | ||
![]() |
175d8ce572 | ||
![]() |
1708a401cf | ||
![]() |
141278e668 | ||
![]() |
754bd82699 | ||
![]() |
999efb6660 | ||
![]() |
0b391a9ef3 | ||
![]() |
2a99ac4430 | ||
![]() |
a5589d0865 | ||
![]() |
83541a0d5d | ||
![]() |
ac0dff7aa1 | ||
![]() |
f22b5157f5 | ||
![]() |
659d0d6115 | ||
![]() |
fd8c99fd8d | ||
![]() |
9494f3a299 | ||
![]() |
8021848b03 | ||
![]() |
5a127c26e6 | ||
![]() |
a7d734c20c | ||
![]() |
7c7129f9a1 | ||
![]() |
05cbc7891d | ||
![]() |
14623456ff | ||
![]() |
5064ec3ac4 | ||
![]() |
892888796d | ||
![]() |
4f2826d2c2 | ||
![]() |
7f824d725b | ||
![]() |
da4096c4ef |
5
.github/CONTRIBUTING.md
vendored
5
.github/CONTRIBUTING.md
vendored
@@ -13,6 +13,9 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
|
|||||||
* Check if this issue/feature is already fixed/implemented in the repository
|
* Check if this issue/feature is already fixed/implemented in the repository
|
||||||
* If you are an android/java developer you are always welcome to fix/implement an issue/a feature yourself
|
* If you are an android/java developer you are always welcome to fix/implement an issue/a feature yourself
|
||||||
|
|
||||||
|
## Bugfixing
|
||||||
|
* If you want to help NewPipe getting bug free, you can send me a mail to tnp@newpipe.schabi.org to let me know that you intent to help, and than register at our [sentry](https://support.schabi.org) setup.
|
||||||
|
|
||||||
## Translation
|
## Translation
|
||||||
|
|
||||||
* NewPipe can be translated on [weblate](https://hosted.weblate.org/projects/newpipe/strings/)
|
* NewPipe can be translated on [weblate](https://hosted.weblate.org/projects/newpipe/strings/)
|
||||||
@@ -31,6 +34,6 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras
|
|||||||
|
|
||||||
## Communication
|
## Communication
|
||||||
|
|
||||||
* I hereby declare our Slack channel as dead!!! There are no plans on building a new chat, but if there is interest on creating one and keeping it alive, I'd be pleased to create one again.
|
* WE DO NOW HAVE A MAILING LIST: [newpipe@list.schabi.org](https://list.schabi.org/cgi-bin/mailman/listinfo/newpipe).
|
||||||
* If you want to get in contact with me or one of our other contributors you can send me an email at tnp(at)schabi.org
|
* If you want to get in contact with me or one of our other contributors you can send me an email at tnp(at)schabi.org
|
||||||
* Feel free to post suggestions, changes, ideas etc!
|
* Feel free to post suggestions, changes, ideas etc!
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@
|
|||||||
/.idea
|
/.idea
|
||||||
/*.iml
|
/*.iml
|
||||||
gradle.properties
|
gradle.properties
|
||||||
|
*~
|
||||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "app/src/main/java/org/schabi/newpipe/extractor"]
|
||||||
|
path = app/src/main/java/org/schabi/newpipe/extractor
|
||||||
|
url = https://github.com/TeamNewPipe/NewPipeExtractor.git
|
24
.travis.yml
24
.travis.yml
@@ -5,35 +5,15 @@ android:
|
|||||||
components:
|
components:
|
||||||
# The BuildTools version used by NewPipe
|
# The BuildTools version used by NewPipe
|
||||||
- tools
|
- tools
|
||||||
- build-tools-23.0.3
|
- build-tools-25.0.2
|
||||||
|
|
||||||
# The SDK version used to compile NewPipe
|
# The SDK version used to compile NewPipe
|
||||||
- android-25
|
- android-25
|
||||||
|
|
||||||
# Additional components
|
# Additional components
|
||||||
- extra-android-support
|
|
||||||
- extra-android-m2repository
|
- extra-android-m2repository
|
||||||
- extra-google-m2repository
|
|
||||||
|
|
||||||
# Emulators
|
script: ./gradlew -Dorg.gradle.jvmargs=-Xmx1536m assembleDebug lintDebug testDebugUnitTest
|
||||||
- sys-img-armeabi-v7a-android-21
|
|
||||||
- sys-img-armeabi-v7a-android-19
|
|
||||||
- sys-img-armeabi-v7a-android-15
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- ADB_INSTALL_TIMEOUT=8 # minutes (2 by default)
|
|
||||||
- GRADLE_OPTS=-Xmx512m # give gradle more memory since it seem to fail otherwise
|
|
||||||
matrix:
|
|
||||||
- ANDROID_TARGET=android-21 ANDROID_ABI=armeabi-v7a
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
|
|
||||||
- emulator -avd test -no-skin -no-audio -no-window &
|
|
||||||
- android-wait-for-emulator
|
|
||||||
- adb shell input keyevent 82 &
|
|
||||||
|
|
||||||
script: ./gradlew --info build connectedCheck
|
|
||||||
|
|
||||||
licenses:
|
licenses:
|
||||||
- '.+'
|
- '.+'
|
||||||
|
BIN
CCC_NewPipe_presentation.pdf
Normal file
BIN
CCC_NewPipe_presentation.pdf
Normal file
Binary file not shown.
25
README.md
25
README.md
@@ -19,11 +19,16 @@ Project status:
|
|||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
[<img src="screenshots/screenshot_1.png" width=150>](screenshots/screenshot_1.png)
|
[<img src="screenshots/screenshot_1.png" width=160>](screenshots/screenshot_1.png)
|
||||||
[<img src="screenshots/screenshot_2.png" width=150>](screenshots/screenshot_2.png)
|
[<img src="screenshots/screenshot_2.png" width=160>](screenshots/screenshot_2.png)
|
||||||
[<img src="screenshots/screenshot_3.png" width=150>](screenshots/screenshot_3.png)
|
[<img src="screenshots/screenshot_3.png" width=160>](screenshots/screenshot_3.png)
|
||||||
[<img src="screenshots/screenshot_4.png" width=150>](screenshots/screenshot_4.png)
|
[<img src="screenshots/screenshot_4.png" width=160>](screenshots/screenshot_4.png)
|
||||||
[<img src="screenshots/screenshot_5.png" width=150>](screenshots/screenshot_5.png)
|
[<img src="screenshots/screenshot_5.png" width=160>](screenshots/screenshot_5.png)
|
||||||
|
[<img src="screenshots/screenshot_6.png" width=160>](screenshots/screenshot_6.png)
|
||||||
|
[<img src="screenshots/screenshot_7.png" width=160>](screenshots/screenshot_7.png)
|
||||||
|
[<img src="screenshots/screenshot_8.png" width=160>](screenshots/screenshot_8.png)
|
||||||
|
[<img src="screenshots/screenshot_9.png" width=160>](screenshots/screenshot_9.png)
|
||||||
|
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
@@ -35,6 +40,7 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
|||||||
* Display general information about a video
|
* Display general information about a video
|
||||||
* Watch YouTube videos
|
* Watch YouTube videos
|
||||||
* Listen to YouTube videos (experimental)
|
* Listen to YouTube videos (experimental)
|
||||||
|
* Popup mode (floating player)
|
||||||
* Select the streaming player to watch the video with
|
* Select the streaming player to watch the video with
|
||||||
* Download videos
|
* Download videos
|
||||||
* Download audio only
|
* Download audio only
|
||||||
@@ -43,18 +49,21 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
|
|||||||
* Search YouTube in a specific language
|
* Search YouTube in a specific language
|
||||||
* Watch age restricted material
|
* Watch age restricted material
|
||||||
* Display general information about channels
|
* Display general information about channels
|
||||||
|
* Search channels
|
||||||
|
* Watch videos from a channel
|
||||||
|
* Orbot/Tor support (not yet directly)
|
||||||
|
* 1080p/2k/4k support
|
||||||
|
|
||||||
### Coming Features
|
### Coming Features
|
||||||
|
|
||||||
* Orbot/Tor support
|
|
||||||
* Bookmarks
|
* Bookmarks
|
||||||
* View history
|
* View history
|
||||||
* Search history
|
* Search history
|
||||||
* Search channels
|
|
||||||
* Subscribe to channels
|
* Subscribe to channels
|
||||||
* Watch videos from a channel
|
|
||||||
* Search/Watch Playlists
|
* Search/Watch Playlists
|
||||||
* Queeing videos
|
* Queeing videos
|
||||||
|
* Subtitles support
|
||||||
|
* livestream support
|
||||||
* ... and many more
|
* ... and many more
|
||||||
|
|
||||||
### Multiservice support
|
### Multiservice support
|
||||||
|
@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 25
|
compileSdkVersion 25
|
||||||
buildToolsVersion '23.0.3'
|
buildToolsVersion '25.0.2'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.schabi.newpipe"
|
applicationId "org.schabi.newpipe"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 20
|
versionCode 34
|
||||||
versionName "0.8.6"
|
versionName "0.9.7"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@@ -32,19 +32,23 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
compile 'com.android.support:appcompat-v7:25.0.0'
|
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||||
compile 'com.android.support:support-v4:25.0.0'
|
testCompile 'org.json:json:20160810'
|
||||||
compile 'com.android.support:design:25.0.0'
|
|
||||||
compile 'com.android.support:recyclerview-v7:25.0.0'
|
compile 'com.android.support:appcompat-v7:25.3.1'
|
||||||
|
compile 'com.android.support:support-v4:25.3.1'
|
||||||
|
compile 'com.android.support:design:25.3.1'
|
||||||
|
compile 'com.android.support:recyclerview-v7:25.3.1'
|
||||||
|
|
||||||
|
compile 'com.google.code.gson:gson:2.7'
|
||||||
compile 'org.jsoup:jsoup:1.8.3'
|
compile 'org.jsoup:jsoup:1.8.3'
|
||||||
compile 'org.mozilla:rhino:1.7.7'
|
compile 'org.mozilla:rhino:1.7.7'
|
||||||
compile 'info.guardianproject.netcipher:netcipher:1.2'
|
|
||||||
compile 'de.hdodenhof:circleimageview:2.0.0'
|
|
||||||
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:3.0.0'
|
|
||||||
testCompile 'junit:junit:4.12'
|
|
||||||
compile 'ch.acra:acra:4.9.0'
|
compile 'ch.acra:acra:4.9.0'
|
||||||
|
compile 'info.guardianproject.netcipher:netcipher:1.2'
|
||||||
|
|
||||||
|
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
|
||||||
|
compile 'de.hdodenhof:circleimageview:2.0.0'
|
||||||
|
compile 'com.github.nirhart:parallaxscroll:1.0'
|
||||||
|
compile 'com.nononsenseapps:filepicker:3.0.0'
|
||||||
|
compile 'com.google.android.exoplayer:exoplayer:r2.3.1'
|
||||||
}
|
}
|
||||||
|
@@ -1,52 +0,0 @@
|
|||||||
package org.schabi.newpipe.extractor.youtube;
|
|
||||||
|
|
||||||
import android.test.AndroidTestCase;
|
|
||||||
|
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 11.03.16.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* YoutubeStreamExtractorLiveStreamTest.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 YoutubeStreamExtractorLiveStreamTest extends AndroidTestCase {
|
|
||||||
|
|
||||||
private StreamExtractor extractor;
|
|
||||||
|
|
||||||
public void setUp() throws IOException, ExtractionException {
|
|
||||||
//todo: make the extractor not throw over a livestream
|
|
||||||
/*
|
|
||||||
|
|
||||||
NewPipe.init(Downloader.getInstance());
|
|
||||||
extractor = NewPipe.getService("Youtube")
|
|
||||||
.getExtractorInstance("https://www.youtube.com/watch?v=J0s6NjqdjLE", Downloader.getInstance());
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testStreamType() throws ParsingException {
|
|
||||||
assertTrue(true);
|
|
||||||
// assertTrue(extractor.getStreamType() == AbstractVideoInfo.StreamType.LIVE_STREAM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@@ -20,10 +20,6 @@ package org.schabi.newpipe;
|
|||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton:
|
* Singleton:
|
||||||
* Used to send data between certain Activity/Services within the same process.
|
* Used to send data between certain Activity/Services within the same process.
|
||||||
@@ -39,8 +35,5 @@ public class ActivityCommunicator {
|
|||||||
return activityCommunicator;
|
return activityCommunicator;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thumbnail send from VideoItemDetailFragment to BackgroundPlayer
|
|
||||||
public volatile Bitmap backgroundPlayerThumbnail;
|
|
||||||
|
|
||||||
public volatile Class returnActivity;
|
public volatile Class returnActivity;
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@ import org.schabi.newpipe.extractor.NewPipe;
|
|||||||
import org.schabi.newpipe.report.AcraReportSenderFactory;
|
import org.schabi.newpipe.report.AcraReportSenderFactory;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import info.guardianproject.netcipher.NetCipher;
|
import info.guardianproject.netcipher.NetCipher;
|
||||||
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
import info.guardianproject.netcipher.proxy.OrbotHelper;
|
||||||
@@ -48,6 +49,7 @@ public class App extends Application {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
// init crashreport
|
// init crashreport
|
||||||
try {
|
try {
|
||||||
final ACRAConfiguration acraConfig = new ConfigurationBuilder(this)
|
final ACRAConfiguration acraConfig = new ConfigurationBuilder(this)
|
||||||
@@ -81,6 +83,8 @@ public class App extends Application {
|
|||||||
// DO NOT REMOVE THIS FUNCTION!!!
|
// DO NOT REMOVE THIS FUNCTION!!!
|
||||||
// Otherwise downloadPathPreference has invalid value.
|
// Otherwise downloadPathPreference has invalid value.
|
||||||
SettingsActivity.initSettings(this);
|
SettingsActivity.initSettings(this);
|
||||||
|
|
||||||
|
ThemeHelper.setTheme(getApplicationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,14 @@
|
|||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||||
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Christian Schabesberger on 01.08.16.
|
* Created by Christian Schabesberger on 01.08.16.
|
||||||
@@ -33,11 +33,11 @@ import org.schabi.newpipe.extractor.NewPipe;
|
|||||||
public class ImageErrorLoadingListener implements ImageLoadingListener {
|
public class ImageErrorLoadingListener implements ImageLoadingListener {
|
||||||
|
|
||||||
private int serviceId = -1;
|
private int serviceId = -1;
|
||||||
private Activity activity = null;
|
private Context context = null;
|
||||||
private View rootView = null;
|
private View rootView = null;
|
||||||
|
|
||||||
public ImageErrorLoadingListener(Activity activity, View rootView, int serviceId) {
|
public ImageErrorLoadingListener(Context context, View rootView, int serviceId) {
|
||||||
this.activity = activity;
|
this.context = context;
|
||||||
this.serviceId= serviceId;
|
this.serviceId= serviceId;
|
||||||
this.rootView = rootView;
|
this.rootView = rootView;
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ public class ImageErrorLoadingListener implements ImageLoadingListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||||
ErrorActivity.reportError(activity,
|
ErrorActivity.reportError(context,
|
||||||
failReason.getCause(), null, rootView,
|
failReason.getCause(), null, rootView,
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
|
ErrorActivity.ErrorInfo.make(ErrorActivity.LOAD_IMAGE,
|
||||||
NewPipe.getNameOfService(serviceId), imageUri,
|
NewPipe.getNameOfService(serviceId), imageUri,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -2,11 +2,13 @@ package org.schabi.newpipe;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.NavUtils;
|
import android.support.v4.app.NavUtils;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.webkit.CookieManager;
|
import android.webkit.CookieManager;
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
@@ -34,6 +36,8 @@ import android.webkit.WebViewClient;
|
|||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
public class ReCaptchaActivity extends AppCompatActivity {
|
public class ReCaptchaActivity extends AppCompatActivity {
|
||||||
|
public static final int RECAPTCHA_REQUEST = 10;
|
||||||
|
|
||||||
public static final String TAG = ReCaptchaActivity.class.toString();
|
public static final String TAG = ReCaptchaActivity.class.toString();
|
||||||
public static final String YT_URL = "https://www.youtube.com";
|
public static final String YT_URL = "https://www.youtube.com";
|
||||||
|
|
||||||
@@ -42,10 +46,18 @@ public class ReCaptchaActivity extends AppCompatActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_recaptcha);
|
setContentView(R.layout.activity_recaptcha);
|
||||||
|
|
||||||
|
// Set return to Cancel by default
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
if (actionBar != null) {
|
||||||
actionBar.setTitle(R.string.reCaptcha_title);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
actionBar.setDisplayShowTitleEnabled(true);
|
actionBar.setTitle(R.string.reCaptcha_title);
|
||||||
|
actionBar.setDisplayShowTitleEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
WebView myWebView = (WebView) findViewById(R.id.reCaptchaWebView);
|
WebView myWebView = (WebView) findViewById(R.id.reCaptchaWebView);
|
||||||
|
|
||||||
@@ -80,19 +92,26 @@ public class ReCaptchaActivity extends AppCompatActivity {
|
|||||||
context = ctx;
|
context = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||||
|
// TODO: Start Loader
|
||||||
|
super.onPageStarted(view, url, favicon);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageFinished(WebView view, String url) {
|
public void onPageFinished(WebView view, String url) {
|
||||||
String cookies = CookieManager.getInstance().getCookie(url);
|
String cookies = CookieManager.getInstance().getCookie(url);
|
||||||
|
|
||||||
|
// TODO: Stop Loader
|
||||||
|
|
||||||
// find cookies : s_gl & goojf and Add cookies to Downloader
|
// find cookies : s_gl & goojf and Add cookies to Downloader
|
||||||
if (find_access_cookies(cookies)) {
|
if (find_access_cookies(cookies)) {
|
||||||
// Give cookies to Downloader class
|
// Give cookies to Downloader class
|
||||||
Downloader.setCookies(mCookies);
|
Downloader.setCookies(mCookies);
|
||||||
|
|
||||||
// Closing activity and return to parent.
|
// Closing activity and return to parent
|
||||||
Intent intent = new Intent(context, org.schabi.newpipe.MainActivity.class);
|
setResult(RESULT_OK);
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
finish();
|
||||||
NavUtils.navigateUpTo(context, intent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
124
app/src/main/java/org/schabi/newpipe/RouterActivity.java
Normal file
124
app/src/main/java/org/schabi/newpipe/RouterActivity.java
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||||
|
* RouterActivity .java is part of NewPipe.
|
||||||
|
*
|
||||||
|
* OpenHitboxStreams 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.
|
||||||
|
*
|
||||||
|
* OpenHitboxStreams 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 OpenHitboxStreams. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Acitivty is designed to route share/open intents to the specified service, and
|
||||||
|
* to the part of the service which can handle the url.
|
||||||
|
*/
|
||||||
|
public class RouterActivity extends Activity {
|
||||||
|
//private static final String TAG = "RouterActivity"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes invisible separators (\p{Z}) and punctuation characters including
|
||||||
|
* brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
|
||||||
|
* more details.
|
||||||
|
*/
|
||||||
|
private final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
handleIntent(getIntent());
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleIntent(Intent intent) {
|
||||||
|
String videoUrl = "";
|
||||||
|
|
||||||
|
// first gather data and find service
|
||||||
|
if (intent.getData() != null) {
|
||||||
|
// this means the video was called though another app
|
||||||
|
videoUrl = intent.getData().toString();
|
||||||
|
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
||||||
|
//this means that vidoe was called through share menu
|
||||||
|
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||||
|
videoUrl = getUris(extraText)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
NavigationHelper.openByLink(this, videoUrl);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String removeHeadingGibberish(final String input) {
|
||||||
|
int start = 0;
|
||||||
|
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
||||||
|
if (!input.substring(i, i + 1).matches("\\p{L}")) {
|
||||||
|
start = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return input.substring(start, input.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String trim(final String input) {
|
||||||
|
if (input == null || input.length() < 1) {
|
||||||
|
return input;
|
||||||
|
} else {
|
||||||
|
String output = input;
|
||||||
|
while (output.length() > 0 && output.substring(0, 1).matches(REGEX_REMOVE_FROM_URL)) {
|
||||||
|
output = output.substring(1);
|
||||||
|
}
|
||||||
|
while (output.length() > 0
|
||||||
|
&& output.substring(output.length() - 1, output.length()).matches(REGEX_REMOVE_FROM_URL)) {
|
||||||
|
output = output.substring(0, output.length() - 1);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all Strings which look remotely like URLs from a text.
|
||||||
|
* Used if NewPipe was called through share menu.
|
||||||
|
*
|
||||||
|
* @param sharedText text to scan for URLs.
|
||||||
|
* @return potential URLs
|
||||||
|
*/
|
||||||
|
private String[] getUris(final String sharedText) {
|
||||||
|
final Collection<String> result = new HashSet<>();
|
||||||
|
if (sharedText != null) {
|
||||||
|
final String[] array = sharedText.split("\\p{Space}");
|
||||||
|
for (String s : array) {
|
||||||
|
s = trim(s);
|
||||||
|
if (s.length() != 0) {
|
||||||
|
if (s.matches(".+://.+")) {
|
||||||
|
result.add(removeHeadingGibberish(s));
|
||||||
|
} else if (s.matches(".+\\..+")) {
|
||||||
|
result.add("http://" + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toArray(new String[result.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
130
app/src/main/java/org/schabi/newpipe/RouterPopupActivity.java
Normal file
130
app/src/main/java/org/schabi/newpipe/RouterPopupActivity.java
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||||
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This activity is thought to open video streams form an external app using the popup player.
|
||||||
|
*/
|
||||||
|
public class RouterPopupActivity extends Activity {
|
||||||
|
//private static final String TAG = "RouterPopupActivity";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes invisible separators (\p{Z}) and punctuation characters including
|
||||||
|
* brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
|
||||||
|
* more details.
|
||||||
|
*/
|
||||||
|
private final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
handleIntent(getIntent());
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void handleIntent(Intent intent) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||||
|
&& !PermissionHelper.checkSystemAlertWindowPermission(this)) {
|
||||||
|
Toast.makeText(this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String videoUrl = "";
|
||||||
|
StreamingService service;
|
||||||
|
|
||||||
|
// first gather data and find service
|
||||||
|
if (intent.getData() != null) {
|
||||||
|
// this means the video was called though another app
|
||||||
|
videoUrl = intent.getData().toString();
|
||||||
|
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
||||||
|
//this means that vidoe was called through share menu
|
||||||
|
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||||
|
videoUrl = getUris(extraText)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
service = NewPipe.getServiceByUrl(videoUrl);
|
||||||
|
if (service == null) {
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent callIntent = new Intent(this, PopupVideoPlayer.class);
|
||||||
|
switch (service.getLinkTypeByUrl(videoUrl)) {
|
||||||
|
case STREAM:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callIntent.putExtra(Constants.KEY_URL, videoUrl);
|
||||||
|
callIntent.putExtra(Constants.KEY_SERVICE_ID, service.getServiceId());
|
||||||
|
startService(callIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String removeHeadingGibberish(final String input) {
|
||||||
|
int start = 0;
|
||||||
|
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
||||||
|
if (!input.substring(i, i + 1).matches("\\p{L}")) {
|
||||||
|
start = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return input.substring(start, input.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String trim(final String input) {
|
||||||
|
if (input == null || input.length() < 1) {
|
||||||
|
return input;
|
||||||
|
} else {
|
||||||
|
String output = input;
|
||||||
|
while (output.length() > 0 && output.substring(0, 1).matches(REGEX_REMOVE_FROM_URL)) {
|
||||||
|
output = output.substring(1);
|
||||||
|
}
|
||||||
|
while (output.length() > 0
|
||||||
|
&& output.substring(output.length() - 1, output.length()).matches(REGEX_REMOVE_FROM_URL)) {
|
||||||
|
output = output.substring(0, output.length() - 1);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all Strings which look remotely like URLs from a text.
|
||||||
|
* Used if NewPipe was called through share menu.
|
||||||
|
*
|
||||||
|
* @param sharedText text to scan for URLs.
|
||||||
|
* @return potential URLs
|
||||||
|
*/
|
||||||
|
private String[] getUris(final String sharedText) {
|
||||||
|
final Collection<String> result = new HashSet<>();
|
||||||
|
if (sharedText != null) {
|
||||||
|
final String[] array = sharedText.split("\\p{Space}");
|
||||||
|
for (String s : array) {
|
||||||
|
s = trim(s);
|
||||||
|
if (s.length() != 0) {
|
||||||
|
if (s.matches(".+://.+")) {
|
||||||
|
result.add(removeHeadingGibberish(s));
|
||||||
|
} else if (s.matches(".+\\..+")) {
|
||||||
|
result.add("http://" + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toArray(new String[result.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,234 +0,0 @@
|
|||||||
package org.schabi.newpipe.detail;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 18.08.15.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
|
||||||
* DetailsMenuHandler.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
class ActionBarHandler {
|
|
||||||
private static final String TAG = ActionBarHandler.class.toString();
|
|
||||||
|
|
||||||
private AppCompatActivity activity;
|
|
||||||
private int selectedVideoStream = -1;
|
|
||||||
|
|
||||||
private SharedPreferences defaultPreferences;
|
|
||||||
|
|
||||||
private Menu menu;
|
|
||||||
|
|
||||||
// Only callbacks are listed here, there are more actions which don't need a callback.
|
|
||||||
// those are edited directly. Typically VideoItemDetailFragment will implement those callbacks.
|
|
||||||
private OnActionListener onShareListener;
|
|
||||||
private OnActionListener onOpenInBrowserListener;
|
|
||||||
private OnActionListener onDownloadListener;
|
|
||||||
private OnActionListener onPlayWithKodiListener;
|
|
||||||
private OnActionListener onPlayAudioListener;
|
|
||||||
|
|
||||||
|
|
||||||
// Triggered when a stream related action is triggered.
|
|
||||||
public interface OnActionListener {
|
|
||||||
void onActionSelected(int selectedStreamId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionBarHandler(AppCompatActivity activity) {
|
|
||||||
this.activity = activity;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"deprecation", "ConstantConditions"})
|
|
||||||
public void setupNavMenu(AppCompatActivity activity) {
|
|
||||||
this.activity = activity;
|
|
||||||
try {
|
|
||||||
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setupStreamList(final List<VideoStream> videoStreams) {
|
|
||||||
if (activity != null) {
|
|
||||||
selectedVideoStream = 0;
|
|
||||||
|
|
||||||
|
|
||||||
// this array will be shown in the dropdown menu for selecting the stream/resolution.
|
|
||||||
String[] itemArray = new String[videoStreams.size()];
|
|
||||||
for (int i = 0; i < videoStreams.size(); i++) {
|
|
||||||
VideoStream item = videoStreams.get(i);
|
|
||||||
itemArray[i] = MediaFormat.getNameById(item.format) + " " + item.resolution;
|
|
||||||
}
|
|
||||||
int defaultResolution = getDefaultResolution(videoStreams);
|
|
||||||
|
|
||||||
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
|
|
||||||
android.R.layout.simple_spinner_dropdown_item, itemArray);
|
|
||||||
|
|
||||||
ActionBar ab = activity.getSupportActionBar();
|
|
||||||
//todo: make this throwsable
|
|
||||||
assert ab != null : "Could not get actionbar";
|
|
||||||
ab.setListNavigationCallbacks(itemAdapter
|
|
||||||
, new ActionBar.OnNavigationListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
|
|
||||||
selectedVideoStream = (int) itemId;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ab.setSelectedNavigationItem(defaultResolution);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private int getDefaultResolution(final List<VideoStream> videoStreams) {
|
|
||||||
if (defaultPreferences == null)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
String defaultResolution = defaultPreferences
|
|
||||||
.getString(activity.getString(R.string.default_resolution_key),
|
|
||||||
activity.getString(R.string.default_resolution_value));
|
|
||||||
|
|
||||||
for (int i = 0; i < videoStreams.size(); i++) {
|
|
||||||
VideoStream item = videoStreams.get(i);
|
|
||||||
if (defaultResolution.equals(item.resolution)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// this is actually an error,
|
|
||||||
// but maybe there is really no stream fitting to the default value.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setupMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
this.menu = menu;
|
|
||||||
|
|
||||||
// CAUTION set item properties programmatically otherwise it would not be accepted by
|
|
||||||
// appcompat itemsinflater.inflate(R.menu.videoitem_detail, menu);
|
|
||||||
|
|
||||||
defaultPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
|
||||||
inflater.inflate(R.menu.videoitem_detail, menu);
|
|
||||||
|
|
||||||
showPlayWithKodiAction(defaultPreferences
|
|
||||||
.getBoolean(activity.getString(R.string.show_play_with_kodi_key), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onItemSelected(MenuItem item) {
|
|
||||||
int id = item.getItemId();
|
|
||||||
switch (id) {
|
|
||||||
case R.id.menu_item_share: {
|
|
||||||
/*
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(Intent.ACTION_SEND);
|
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, websiteUrl);
|
|
||||||
intent.setType("text/plain");
|
|
||||||
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.share_dialog_title)));
|
|
||||||
*/
|
|
||||||
if(onShareListener != null) {
|
|
||||||
onShareListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case R.id.menu_item_openInBrowser: {
|
|
||||||
if(onOpenInBrowserListener != null) {
|
|
||||||
onOpenInBrowserListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case R.id.menu_item_download:
|
|
||||||
if(onDownloadListener != null) {
|
|
||||||
onDownloadListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case R.id.action_settings: {
|
|
||||||
Intent intent = new Intent(activity, SettingsActivity.class);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case R.id.action_play_with_kodi:
|
|
||||||
if(onPlayWithKodiListener != null) {
|
|
||||||
onPlayWithKodiListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case R.id.menu_item_play_audio:
|
|
||||||
if(onPlayAudioListener != null) {
|
|
||||||
onPlayAudioListener.onActionSelected(selectedVideoStream);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case R.id.menu_item_downloads: {
|
|
||||||
Intent intent =
|
|
||||||
new Intent(activity, org.schabi.newpipe.download.DownloadActivity.class);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
Log.e(TAG, "Menu Item not known");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSelectedVideoStream() {
|
|
||||||
return selectedVideoStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnShareListener(OnActionListener listener) {
|
|
||||||
onShareListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnOpenInBrowserListener(OnActionListener listener) {
|
|
||||||
onOpenInBrowserListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnDownloadListener(OnActionListener listener) {
|
|
||||||
onDownloadListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnPlayWithKodiListener(OnActionListener listener) {
|
|
||||||
onPlayWithKodiListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnPlayAudioListener(OnActionListener listener) {
|
|
||||||
onPlayAudioListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showAudioAction(boolean visible) {
|
|
||||||
menu.findItem(R.id.menu_item_play_audio).setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showDownloadAction(boolean visible) {
|
|
||||||
menu.findItem(R.id.menu_item_download).setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showPlayWithKodiAction(boolean visible) {
|
|
||||||
menu.findItem(R.id.action_play_with_kodi).setVisible(visible);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,220 +0,0 @@
|
|||||||
package org.schabi.newpipe.detail;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.Downloader;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamExtractor;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 02.08.16.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* StreamInfoWorker.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 StreamInfoWorker {
|
|
||||||
|
|
||||||
private static final String TAG = StreamInfoWorker.class.toString();
|
|
||||||
|
|
||||||
public interface OnStreamInfoReceivedListener {
|
|
||||||
void onReceive(StreamInfo info);
|
|
||||||
void onError(int messageId);
|
|
||||||
void onBlockedByGemaError();
|
|
||||||
void onContentErrorWithMessage(int messageId);
|
|
||||||
void onContentError();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class StreamExtractorRunnable implements Runnable {
|
|
||||||
private final Handler h = new Handler();
|
|
||||||
private StreamExtractor streamExtractor;
|
|
||||||
private final int serviceId;
|
|
||||||
private final String videoUrl;
|
|
||||||
private Activity a;
|
|
||||||
|
|
||||||
public StreamExtractorRunnable(Activity a, String videoUrl, int serviceId) {
|
|
||||||
this.serviceId = serviceId;
|
|
||||||
this.videoUrl = videoUrl;
|
|
||||||
this.a = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
StreamInfo streamInfo = null;
|
|
||||||
StreamingService service = null;
|
|
||||||
try {
|
|
||||||
service = NewPipe.getService(serviceId);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
"", videoUrl, R.string.could_not_get_stream));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
streamExtractor = service.getExtractorInstance(videoUrl);
|
|
||||||
streamInfo = StreamInfo.getVideoInfo(streamExtractor);
|
|
||||||
|
|
||||||
final StreamInfo info = streamInfo;
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener.onReceive(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// look for errors during extraction
|
|
||||||
// this if statement only covers extra information.
|
|
||||||
// if these are not available or caused an error, they are just not available
|
|
||||||
// but don't render the stream information unusalbe.
|
|
||||||
if(streamInfo != null &&
|
|
||||||
!streamInfo.errors.isEmpty()) {
|
|
||||||
Log.e(TAG, "OCCURRED ERRORS DURING EXTRACTION:");
|
|
||||||
for (Throwable e : streamInfo.errors) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.e(TAG, "------");
|
|
||||||
}
|
|
||||||
|
|
||||||
View rootView = a != null ? a.findViewById(R.id.video_item_detail) : null;
|
|
||||||
ErrorActivity.reportError(h, a,
|
|
||||||
streamInfo.errors, null, rootView,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, 0 /* no message for the user */));
|
|
||||||
}
|
|
||||||
|
|
||||||
// These errors render the stream information unusable.
|
|
||||||
} catch (IOException e) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener.onError(R.string.network_error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
// custom service related exceptions
|
|
||||||
catch (YoutubeStreamExtractor.DecryptException de) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener.onError(R.string.youtube_signature_decryption_error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
de.printStackTrace();
|
|
||||||
} catch (YoutubeStreamExtractor.GemaException ge) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener.onBlockedByGemaError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch(YoutubeStreamExtractor.LiveStreamException e) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener
|
|
||||||
.onContentErrorWithMessage(R.string.live_streams_not_supported);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// ----------------------------------------
|
|
||||||
catch(StreamExtractor.ContentNotAvailableException e) {
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onStreamInfoReceivedListener
|
|
||||||
.onContentError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch(StreamInfo.StreamExctractException e) {
|
|
||||||
if(!streamInfo.errors.isEmpty()) {
|
|
||||||
// !!! if this case ever kicks in someone gets kicked out !!!
|
|
||||||
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
|
|
||||||
} else {
|
|
||||||
ErrorActivity.reportError(h, a, streamInfo.errors, VideoItemDetailFragment.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.could_not_get_stream));
|
|
||||||
}
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
a.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ParsingException e) {
|
|
||||||
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.parsing_error));
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
a.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch(Exception e) {
|
|
||||||
ErrorActivity.reportError(h, a, e, VideoItemDetailFragment.class, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.REQUESTED_STREAM,
|
|
||||||
service.getServiceInfo().name, videoUrl, R.string.general_error));
|
|
||||||
h.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
a.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StreamInfoWorker streamInfoWorker = null;
|
|
||||||
private StreamExtractorRunnable runnable = null;
|
|
||||||
private OnStreamInfoReceivedListener onStreamInfoReceivedListener = null;
|
|
||||||
|
|
||||||
private StreamInfoWorker() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static StreamInfoWorker getInstance() {
|
|
||||||
return streamInfoWorker == null ? (streamInfoWorker = new StreamInfoWorker()) : streamInfoWorker;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void search(int serviceId, String url, Activity a) {
|
|
||||||
runnable = new StreamExtractorRunnable(a, url, serviceId);
|
|
||||||
Thread thread = new Thread(runnable);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnStreamInfoReceivedListener(
|
|
||||||
OnStreamInfoReceivedListener onStreamInfoReceivedListener) {
|
|
||||||
this.onStreamInfoReceivedListener = onStreamInfoReceivedListener;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,242 +0,0 @@
|
|||||||
package org.schabi.newpipe.detail;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.media.AudioManager;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v4.app.NavUtils;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.App;
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
|
||||||
* VideoItemDetailActivity.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 VideoItemDetailActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes invisible separators (\p{Z}) and punctuation characters including
|
|
||||||
* brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
|
|
||||||
* more details.
|
|
||||||
*/
|
|
||||||
private final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
|
|
||||||
|
|
||||||
private static final String TAG = VideoItemDetailActivity.class.toString();
|
|
||||||
|
|
||||||
private VideoItemDetailFragment fragment;
|
|
||||||
|
|
||||||
private String videoUrl;
|
|
||||||
private int currentStreamingService = -1;
|
|
||||||
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.activity_videoitem_detail);
|
|
||||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
|
||||||
// Show the Up button in the action bar.
|
|
||||||
try {
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
||||||
} catch(Exception e) {
|
|
||||||
Log.d(TAG, "Could not get SupportActionBar");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
// savedInstanceState is non-null when there is fragment state
|
|
||||||
// saved from previous configurations of this activity
|
|
||||||
// (e.g. when rotating the screen from portrait to landscape).
|
|
||||||
// In this case, the fragment will automatically be re-added
|
|
||||||
// to its container so we don't need to manually add it.
|
|
||||||
// For more information, see the Fragments API guide at:
|
|
||||||
//
|
|
||||||
// http://developer.android.com/guide/components/fragments.html
|
|
||||||
//
|
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
handleIntent(getIntent());
|
|
||||||
} else {
|
|
||||||
videoUrl = savedInstanceState.getString(VideoItemDetailFragment.VIDEO_URL);
|
|
||||||
currentStreamingService = savedInstanceState.getInt(VideoItemDetailFragment.STREAMING_SERVICE);
|
|
||||||
addFragment(savedInstanceState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onNewIntent(Intent intent) {
|
|
||||||
super.onNewIntent(intent);
|
|
||||||
setIntent(intent);
|
|
||||||
handleIntent(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleIntent(Intent intent) {
|
|
||||||
Bundle arguments = new Bundle();
|
|
||||||
boolean autoplay = false;
|
|
||||||
if (intent.getData() != null) {
|
|
||||||
// this means the video was called though another app
|
|
||||||
videoUrl = intent.getData().toString();
|
|
||||||
currentStreamingService = getServiceIdByUrl(videoUrl);
|
|
||||||
if(currentStreamingService == -1) {
|
|
||||||
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
autoplay = PreferenceManager.getDefaultSharedPreferences(this)
|
|
||||||
.getBoolean(getString(R.string.autoplay_through_intent_key), false);
|
|
||||||
} else if(intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
|
|
||||||
//this means that vidoe was called through share menu
|
|
||||||
String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
|
||||||
videoUrl = getUris(extraText)[0];
|
|
||||||
currentStreamingService = getServiceIdByUrl(videoUrl);
|
|
||||||
} else {
|
|
||||||
//this is if the video was called through another NewPipe activity
|
|
||||||
videoUrl = intent.getStringExtra(VideoItemDetailFragment.VIDEO_URL);
|
|
||||||
currentStreamingService = intent.getIntExtra(VideoItemDetailFragment.STREAMING_SERVICE, -1);
|
|
||||||
}
|
|
||||||
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY, autoplay);
|
|
||||||
arguments.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl);
|
|
||||||
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingService);
|
|
||||||
addFragment(arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addFragment(final Bundle arguments) {
|
|
||||||
// Create the detail fragment and add it to the activity
|
|
||||||
// using a fragment transaction.
|
|
||||||
fragment = new VideoItemDetailFragment();
|
|
||||||
fragment.setArguments(arguments);
|
|
||||||
getSupportFragmentManager().beginTransaction()
|
|
||||||
.replace(R.id.videoitem_detail_container, fragment)
|
|
||||||
.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
App.checkStartTor(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
|
||||||
outState.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl);
|
|
||||||
outState.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingService);
|
|
||||||
outState.putBoolean(VideoItemDetailFragment.AUTO_PLAY, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
int id = item.getItemId();
|
|
||||||
if (id == android.R.id.home) {
|
|
||||||
// This ID represents the Home or Up button. In the case of this
|
|
||||||
// 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, MainActivity.class);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
||||||
NavUtils.navigateUpTo(this, intent);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return fragment.onOptionsItemSelected(item) ||
|
|
||||||
super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
super.onCreateOptionsMenu(menu);
|
|
||||||
fragment.onCreateOptionsMenu(menu, getMenuInflater());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves all Strings which look remotely like URLs from a text.
|
|
||||||
* Used if NewPipe was called through share menu.
|
|
||||||
*
|
|
||||||
* @param sharedText text to scan for URLs.
|
|
||||||
* @return potential URLs
|
|
||||||
*/
|
|
||||||
private String[] getUris(final String sharedText) {
|
|
||||||
final Collection<String> result = new HashSet<>();
|
|
||||||
if (sharedText != null) {
|
|
||||||
final String[] array = sharedText.split("\\p{Space}");
|
|
||||||
for (String s : array) {
|
|
||||||
s = trim(s);
|
|
||||||
if (s.length() != 0) {
|
|
||||||
if (s.matches(".+://.+")) {
|
|
||||||
result.add(removeHeadingGibberish(s));
|
|
||||||
} else if (s.matches(".+\\..+")) {
|
|
||||||
result.add("http://" + s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toArray(new String[result.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String removeHeadingGibberish(final String input) {
|
|
||||||
int start = 0;
|
|
||||||
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
|
|
||||||
if (!input.substring(i, i + 1).matches("\\p{L}")) {
|
|
||||||
start = i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return input.substring(start, input.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String trim(final String input) {
|
|
||||||
if (input == null || input.length() < 1) {
|
|
||||||
return input;
|
|
||||||
} else {
|
|
||||||
String output = input;
|
|
||||||
while (output.length() > 0 && output.substring(0, 1).matches(REGEX_REMOVE_FROM_URL)) {
|
|
||||||
output = output.substring(1);
|
|
||||||
}
|
|
||||||
while (output.length() > 0
|
|
||||||
&& output.substring(output.length() - 1, output.length()).matches(REGEX_REMOVE_FROM_URL)) {
|
|
||||||
output = output.substring(0, output.length() - 1);
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getServiceIdByUrl(String url) {
|
|
||||||
StreamingService[] serviceList = NewPipe.getServices();
|
|
||||||
int service = -1;
|
|
||||||
for (int i = 0; i < serviceList.length; i++) {
|
|
||||||
if (serviceList[i].getUrlIdHandlerInstance().acceptUrl(videoUrl)) {
|
|
||||||
service = i;
|
|
||||||
//videoExtractor = ServiceList.getService(i).getExtractorInstance();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@@ -3,15 +3,11 @@ package org.schabi.newpipe.download;
|
|||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.FragmentTransaction;
|
import android.app.FragmentTransaction;
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.NavUtils;
|
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
@@ -28,51 +24,32 @@ import android.widget.SeekBar;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.settings.NewPipeSettings;
|
||||||
import org.schabi.newpipe.settings.SettingsActivity;
|
import org.schabi.newpipe.settings.SettingsActivity;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import us.shandian.giga.get.DownloadManager;
|
|
||||||
import us.shandian.giga.service.DownloadManagerService;
|
import us.shandian.giga.service.DownloadManagerService;
|
||||||
import us.shandian.giga.ui.fragment.AllMissionsFragment;
|
import us.shandian.giga.ui.fragment.AllMissionsFragment;
|
||||||
import us.shandian.giga.ui.fragment.MissionsFragment;
|
import us.shandian.giga.ui.fragment.MissionsFragment;
|
||||||
import us.shandian.giga.util.CrashHandler;
|
import us.shandian.giga.util.CrashHandler;
|
||||||
import us.shandian.giga.util.Utility;
|
import us.shandian.giga.util.Utility;
|
||||||
|
|
||||||
public class DownloadActivity extends AppCompatActivity implements AdapterView.OnItemClickListener{
|
public class DownloadActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
|
||||||
|
|
||||||
public static final String INTENT_DOWNLOAD = "us.shandian.giga.intent.DOWNLOAD";
|
public static final String INTENT_DOWNLOAD = "us.shandian.giga.intent.DOWNLOAD";
|
||||||
|
|
||||||
public static final String INTENT_LIST = "us.shandian.giga.intent.LIST";
|
public static final String INTENT_LIST = "us.shandian.giga.intent.LIST";
|
||||||
|
|
||||||
private static final String TAG = DownloadActivity.class.toString();
|
|
||||||
public static final String THREADS = "threads";
|
public static final String THREADS = "threads";
|
||||||
|
private static final String TAG = DownloadActivity.class.toString();
|
||||||
|
|
||||||
private MissionsFragment mFragment;
|
private MissionsFragment mFragment;
|
||||||
private DownloadManager mManager;
|
|
||||||
private DownloadManagerService.DMBinder mBinder;
|
|
||||||
|
|
||||||
private String mPendingUrl;
|
private String mPendingUrl;
|
||||||
private SharedPreferences mPrefs;
|
private SharedPreferences mPrefs;
|
||||||
|
|
||||||
private ServiceConnection mConnection = new ServiceConnection() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(ComponentName p1, IBinder binder) {
|
|
||||||
mBinder = (DownloadManagerService.DMBinder) binder;
|
|
||||||
mManager = mBinder.getDownloadManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected(ComponentName p1) {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@TargetApi(21)
|
@TargetApi(21)
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -83,19 +60,20 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
|||||||
Intent i = new Intent();
|
Intent i = new Intent();
|
||||||
i.setClass(this, DownloadManagerService.class);
|
i.setClass(this, DownloadManagerService.class);
|
||||||
startService(i);
|
startService(i);
|
||||||
bindService(i, mConnection, Context.BIND_AUTO_CREATE);
|
|
||||||
|
|
||||||
|
ThemeHelper.setTheme(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_downloader);
|
setContentView(R.layout.activity_downloader);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
|
|
||||||
// its ok if this failes, we will catch that error later, and send it as report
|
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
if (actionBar != null) {
|
||||||
actionBar.setTitle(R.string.downloads_title);
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
actionBar.setDisplayShowTitleEnabled(true);
|
actionBar.setTitle(R.string.downloads_title);
|
||||||
|
actionBar.setDisplayShowTitleEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|
||||||
@@ -180,7 +158,7 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
|||||||
name.setText(getIntent().getStringExtra("fileName"));
|
name.setText(getIntent().getStringExtra("fileName"));
|
||||||
|
|
||||||
toolbar.setTitle(R.string.add);
|
toolbar.setTitle(R.string.add);
|
||||||
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_black_24dp);
|
toolbar.setNavigationIcon(ThemeHelper.isLightThemeSelected(this) ? R.drawable.ic_arrow_back_black_24dp : R.drawable.ic_arrow_back_white_24dp);
|
||||||
toolbar.inflateMenu(R.menu.dialog_url);
|
toolbar.inflateMenu(R.menu.dialog_url);
|
||||||
|
|
||||||
// Show the dialog
|
// Show the dialog
|
||||||
@@ -202,25 +180,27 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
|||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem item) {
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
if (item.getItemId() == R.id.okay) {
|
if (item.getItemId() == R.id.okay) {
|
||||||
|
|
||||||
|
String location;
|
||||||
|
if (audioButton.isChecked()) {
|
||||||
|
location = NewPipeSettings.getAudioDownloadPath(DownloadActivity.this);
|
||||||
|
} else {
|
||||||
|
location = NewPipeSettings.getVideoDownloadPath(DownloadActivity.this);
|
||||||
|
}
|
||||||
|
|
||||||
String fName = name.getText().toString().trim();
|
String fName = name.getText().toString().trim();
|
||||||
|
|
||||||
File f = new File(mManager.getLocation() + "/" + fName);
|
File f = new File(location, fName);
|
||||||
|
|
||||||
if (f.exists()) {
|
if (f.exists()) {
|
||||||
Toast.makeText(DownloadActivity.this, R.string.msg_exists, Toast.LENGTH_SHORT).show();
|
Toast.makeText(DownloadActivity.this, R.string.msg_exists, Toast.LENGTH_SHORT).show();
|
||||||
} else {
|
} else {
|
||||||
|
DownloadManagerService.startMission(
|
||||||
while (mBinder == null);
|
DownloadActivity.this,
|
||||||
|
getIntent().getData().toString(), location, fName,
|
||||||
int res = mManager.startMission(
|
audioButton.isChecked(), threads.getProgress() + 1);
|
||||||
getIntent().getData().toString(),
|
|
||||||
fName,
|
|
||||||
audioButton.isChecked(),
|
|
||||||
threads.getProgress() + 1);
|
|
||||||
mBinder.onMissionAdded(mManager.getMission(res));
|
|
||||||
mFragment.notifyChange();
|
mFragment.notifyChange();
|
||||||
|
|
||||||
mPrefs.edit().putInt(THREADS, threads.getProgress() + 1).commit();
|
mPrefs.edit().putInt(THREADS, threads.getProgress() + 1).apply();
|
||||||
mPendingUrl = null;
|
mPendingUrl = null;
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}
|
}
|
||||||
@@ -254,9 +234,7 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
|||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case android.R.id.home: {
|
case android.R.id.home: {
|
||||||
Intent intent = new Intent(this, org.schabi.newpipe.MainActivity.class);
|
onBackPressed();
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
||||||
NavUtils.navigateUpTo(this, intent);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.action_settings: {
|
case R.id.action_settings: {
|
||||||
@@ -264,17 +242,9 @@ public class DownloadActivity extends AppCompatActivity implements AdapterView.O
|
|||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case R.id.action_report_error: {
|
|
||||||
ErrorActivity.reportError(DownloadActivity.this, new Vector<Throwable>(),
|
|
||||||
null, null,
|
|
||||||
ErrorActivity.ErrorInfo.make(ErrorActivity.USER_REPORT,
|
|
||||||
null,
|
|
||||||
"user_report", R.string.user_report));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return mFragment.onOptionsItemSelected(item) ||
|
return super.onOptionsItemSelected(item);
|
||||||
super.onOptionsItemSelected(item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
|||||||
package org.schabi.newpipe.download;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import us.shandian.giga.get.DownloadMission;
|
|
||||||
import us.shandian.giga.get.DownloadMission.MissionListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by erwin on 06.11.16.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* DownloadListener.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class DownloadListener implements MissionListener
|
|
||||||
{
|
|
||||||
DownloadMission mMission;
|
|
||||||
Context mContext;
|
|
||||||
|
|
||||||
public DownloadListener(Context context, DownloadMission mission)
|
|
||||||
{
|
|
||||||
super();
|
|
||||||
mMission = mission;
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProgressUpdate(long done, long total)
|
|
||||||
{
|
|
||||||
// do nothing special ...
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFinish()
|
|
||||||
{
|
|
||||||
// notify media scanner on downloaded media file ...
|
|
||||||
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
|
|
||||||
Uri.parse( "file://" + mMission.location
|
|
||||||
+ "/" + mMission.name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(int errCode)
|
|
||||||
{
|
|
||||||
// do nothing special ...
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,165 +0,0 @@
|
|||||||
package org.schabi.newpipe.download;
|
|
||||||
|
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
|
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
|
||||||
|
|
||||||
import info.guardianproject.netcipher.NetCipher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 14.08.15.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
|
||||||
* FileDownloader.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: FOR HEVEN SAKE !!! DO NOT SIMPLY USE ASYNCTASK. MAKE THIS A PROPER SERVICE !!!
|
|
||||||
public class FileDownloader extends AsyncTask<Void, Integer, Void> {
|
|
||||||
public static final String TAG = "FileDownloader";
|
|
||||||
|
|
||||||
|
|
||||||
private NotificationManager nm;
|
|
||||||
private NotificationCompat.Builder builder;
|
|
||||||
private int notifyId = 0x1234;
|
|
||||||
private int fileSize = 0xffffffff;
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final String fileURL;
|
|
||||||
private final File saveFilePath;
|
|
||||||
private final String title;
|
|
||||||
|
|
||||||
private final String debugContext;
|
|
||||||
|
|
||||||
public FileDownloader(Context context, String fileURL, File saveFilePath, String title) {
|
|
||||||
this.context = context;
|
|
||||||
this.fileURL = fileURL;
|
|
||||||
this.saveFilePath = saveFilePath;
|
|
||||||
this.title = title;
|
|
||||||
|
|
||||||
this.debugContext = "'" + fileURL +
|
|
||||||
"' => '" + saveFilePath + "'";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Downloads a file from a URL in the background using an {@link AsyncTask}.
|
|
||||||
*
|
|
||||||
* @param fileURL HTTP URL of the file to be downloaded
|
|
||||||
* @param saveFilePath path of the directory to save the file
|
|
||||||
* @param title
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public static void downloadFile(final Context context, final String fileURL, final File saveFilePath, String title) {
|
|
||||||
new FileDownloader(context, fileURL, saveFilePath, title).execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** AsyncTask impl: executed in gui thread */
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
Drawable icon = context.getResources().getDrawable(R.mipmap.ic_launcher);
|
|
||||||
builder = new NotificationCompat.Builder(context)
|
|
||||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
|
||||||
.setLargeIcon(((BitmapDrawable) icon).getBitmap())
|
|
||||||
.setContentTitle(saveFilePath.getName())
|
|
||||||
.setContentText(saveFilePath.getAbsolutePath())
|
|
||||||
.setProgress(fileSize, 0, false);
|
|
||||||
nm.notify(notifyId, builder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** AsyncTask impl: executed in background thread does the download */
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... voids) {
|
|
||||||
HttpsURLConnection con = null;
|
|
||||||
InputStream inputStream = null;
|
|
||||||
FileOutputStream outputStream = null;
|
|
||||||
try {
|
|
||||||
con = NetCipher.getHttpsURLConnection(fileURL);
|
|
||||||
int responseCode = con.getResponseCode();
|
|
||||||
|
|
||||||
// always check HTTP response code first
|
|
||||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
|
||||||
fileSize = con.getContentLength();
|
|
||||||
inputStream = new BufferedInputStream(con.getInputStream());
|
|
||||||
outputStream = new FileOutputStream(saveFilePath);
|
|
||||||
|
|
||||||
int bufferSize = 8192;
|
|
||||||
int downloaded = 0;
|
|
||||||
|
|
||||||
int bytesRead = -1;
|
|
||||||
byte[] buffer = new byte[bufferSize];
|
|
||||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
||||||
outputStream.write(buffer, 0, bytesRead);
|
|
||||||
downloaded += bytesRead;
|
|
||||||
if (downloaded % 50000 < bufferSize) {
|
|
||||||
publishProgress(downloaded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
publishProgress(bufferSize);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "No file to download. Server replied HTTP code: " + responseCode);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "No file to download. Server replied HTTP code: ", e);
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (outputStream != null) {
|
|
||||||
outputStream.close();
|
|
||||||
}
|
|
||||||
if (inputStream != null) {
|
|
||||||
inputStream.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
if (con != null) {
|
|
||||||
con.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onProgressUpdate(Integer... progress) {
|
|
||||||
builder.setProgress(fileSize, progress[0], false);
|
|
||||||
nm.notify(notifyId, builder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void aVoid) {
|
|
||||||
super.onPostExecute(aVoid);
|
|
||||||
nm.cancel(notifyId);
|
|
||||||
}
|
|
||||||
}
|
|
1
app/src/main/java/org/schabi/newpipe/extractor
Submodule
1
app/src/main/java/org/schabi/newpipe/extractor
Submodule
Submodule app/src/main/java/org/schabi/newpipe/extractor added at 5907c35dfb
@@ -1,41 +0,0 @@
|
|||||||
package org.schabi.newpipe.extractor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* AbstractStreamInfo.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**Common properties between StreamInfo and StreamPreviewInfo.*/
|
|
||||||
public abstract class AbstractStreamInfo {
|
|
||||||
public static enum StreamType {
|
|
||||||
NONE, // placeholder to check if stream type was checked or not
|
|
||||||
VIDEO_STREAM,
|
|
||||||
AUDIO_STREAM,
|
|
||||||
LIVE_STREAM,
|
|
||||||
AUDIO_LIVE_STREAM,
|
|
||||||
FILE
|
|
||||||
}
|
|
||||||
|
|
||||||
public StreamType stream_type;
|
|
||||||
public int service_id = -1;
|
|
||||||
public String id = "";
|
|
||||||
public String title = "";
|
|
||||||
public String uploader = "";
|
|
||||||
public String thumbnail_url = "";
|
|
||||||
public String webpage_url = "";
|
|
||||||
public String upload_date = "";
|
|
||||||
public long view_count = -1;
|
|
||||||
}
|
|
@@ -1,113 +0,0 @@
|
|||||||
package org.schabi.newpipe.extractor;
|
|
||||||
|
|
||||||
import android.util.Xml;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
|
||||||
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 02.02.16.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* DashMpdParser.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 DashMpdParser {
|
|
||||||
|
|
||||||
private DashMpdParser() {
|
|
||||||
}
|
|
||||||
|
|
||||||
static class DashMpdParsingException extends ParsingException {
|
|
||||||
DashMpdParsingException(String message, Exception e) {
|
|
||||||
super(message, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<AudioStream> getAudioStreams(String dashManifestUrl)
|
|
||||||
throws DashMpdParsingException, ReCaptchaException {
|
|
||||||
String dashDoc;
|
|
||||||
Downloader downloader = NewPipe.getDownloader();
|
|
||||||
try {
|
|
||||||
dashDoc = downloader.download(dashManifestUrl);
|
|
||||||
} catch(IOException ioe) {
|
|
||||||
throw new DashMpdParsingException("Could not get dash mpd: " + dashManifestUrl, ioe);
|
|
||||||
} catch (ReCaptchaException e) {
|
|
||||||
throw new ReCaptchaException("reCaptcha Challenge needed");
|
|
||||||
}
|
|
||||||
Vector<AudioStream> audioStreams = new Vector<>();
|
|
||||||
try {
|
|
||||||
XmlPullParser parser = Xml.newPullParser();
|
|
||||||
parser.setInput(new StringReader(dashDoc));
|
|
||||||
String tagName = "";
|
|
||||||
String currentMimeType = "";
|
|
||||||
int currentBandwidth = -1;
|
|
||||||
int currentSamplingRate = -1;
|
|
||||||
boolean currentTagIsBaseUrl = false;
|
|
||||||
for(int eventType = parser.getEventType();
|
|
||||||
eventType != XmlPullParser.END_DOCUMENT;
|
|
||||||
eventType = parser.next() ) {
|
|
||||||
switch(eventType) {
|
|
||||||
case XmlPullParser.START_TAG:
|
|
||||||
tagName = parser.getName();
|
|
||||||
if(tagName.equals("AdaptationSet")) {
|
|
||||||
currentMimeType = parser.getAttributeValue(XmlPullParser.NO_NAMESPACE, "mimeType");
|
|
||||||
} else if(tagName.equals("Representation") && currentMimeType.contains("audio")) {
|
|
||||||
currentBandwidth = Integer.parseInt(
|
|
||||||
parser.getAttributeValue(XmlPullParser.NO_NAMESPACE, "bandwidth"));
|
|
||||||
currentSamplingRate = Integer.parseInt(
|
|
||||||
parser.getAttributeValue(XmlPullParser.NO_NAMESPACE, "audioSamplingRate"));
|
|
||||||
} else if(tagName.equals("BaseURL")) {
|
|
||||||
currentTagIsBaseUrl = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XmlPullParser.TEXT:
|
|
||||||
// actual stream tag
|
|
||||||
if(currentTagIsBaseUrl &&
|
|
||||||
(currentMimeType.contains("audio"))) {
|
|
||||||
int format = -1;
|
|
||||||
if(currentMimeType.equals(MediaFormat.WEBMA.mimeType)) {
|
|
||||||
format = MediaFormat.WEBMA.id;
|
|
||||||
} else if(currentMimeType.equals(MediaFormat.M4A.mimeType)) {
|
|
||||||
format = MediaFormat.M4A.id;
|
|
||||||
}
|
|
||||||
audioStreams.add(new AudioStream(parser.getText(),
|
|
||||||
format, currentBandwidth, currentSamplingRate));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XmlPullParser.END_TAG:
|
|
||||||
if(tagName.equals("AdaptationSet")) {
|
|
||||||
currentMimeType = "";
|
|
||||||
} else if(tagName.equals("BaseURL")) {
|
|
||||||
currentTagIsBaseUrl = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new DashMpdParsingException("Could not parse Dash mpd", e);
|
|
||||||
}
|
|
||||||
return audioStreams;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,52 +0,0 @@
|
|||||||
package org.schabi.newpipe.extractor;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 28.01.16.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* Downloader.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 interface Downloader {
|
|
||||||
|
|
||||||
/**Download the text file at the supplied URL as in download(String),
|
|
||||||
* but set the HTTP header field "Accept-Language" to the supplied string.
|
|
||||||
* @param siteUrl the URL of the text file to return the contents of
|
|
||||||
* @param language the language (usually a 2-character code) to set as the preferred language
|
|
||||||
* @return the contents of the specified text file
|
|
||||||
* @throws IOException*/
|
|
||||||
String download(String siteUrl, String language) throws IOException, ReCaptchaException;
|
|
||||||
|
|
||||||
/**Download the text file at the supplied URL as in download(String),
|
|
||||||
* but set the HTTP header field "Accept-Language" to the supplied string.
|
|
||||||
* @param siteUrl the URL of the text file to return the contents of
|
|
||||||
* @param customProperties set request header properties
|
|
||||||
* @return the contents of the specified text file
|
|
||||||
* @throws IOException*/
|
|
||||||
String download(String siteUrl, Map<String, String> customProperties) throws IOException, ReCaptchaException;
|
|
||||||
|
|
||||||
/**Download (via HTTP) the text file located at the supplied URL, and return its contents.
|
|
||||||
* Primarily intended for downloading web pages.
|
|
||||||
* @param siteUrl the URL of the text file to download
|
|
||||||
* @return the contents of the specified text file
|
|
||||||
* @throws IOException*/
|
|
||||||
String download(String siteUrl) throws IOException, ReCaptchaException;
|
|
||||||
}
|
|
@@ -1,83 +0,0 @@
|
|||||||
package org.schabi.newpipe.extractor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Adam Howard on 08/11/15.
|
|
||||||
*
|
|
||||||
* Copyright (c) Christian Schabesberger <chris.schabesberger@mailbox.org>
|
|
||||||
* and Adam Howard <achdisposable1@gmail.com> 2015
|
|
||||||
*
|
|
||||||
* MediaFormat.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**Static data about various media formats support by Newpipe, eg mime type, extension*/
|
|
||||||
|
|
||||||
public enum MediaFormat {
|
|
||||||
//video and audio combined formats
|
|
||||||
// id name suffix mime type
|
|
||||||
MPEG_4 (0x0, "MPEG-4", "mp4", "video/mp4"),
|
|
||||||
v3GPP (0x1, "3GPP", "3gp", "video/3gpp"),
|
|
||||||
WEBM (0x2, "WebM", "webm", "video/webm"),
|
|
||||||
// audio formats
|
|
||||||
M4A (0x3, "m4a", "m4a", "audio/mp4"),
|
|
||||||
WEBMA (0x4, "WebM", "webm", "audio/webm");
|
|
||||||
|
|
||||||
public final int id;
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public final String name;
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
public final String suffix;
|
|
||||||
public final String mimeType;
|
|
||||||
|
|
||||||
MediaFormat(int id, String name, String suffix, String mimeType) {
|
|
||||||
this.id = id;
|
|
||||||
this.name = name;
|
|
||||||
this.suffix = suffix;
|
|
||||||
this.mimeType = mimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**Return the friendly name of the media format with the supplied id
|
|
||||||
* @param ident the id of the media format. Currently an arbitrary, NewPipe-specific number.
|
|
||||||
* @return the friendly name of the MediaFormat associated with this ids,
|
|
||||||
* or an empty String if none match it.*/
|
|
||||||
public static String getNameById(int ident) {
|
|
||||||
for (MediaFormat vf : MediaFormat.values()) {
|
|
||||||
if(vf.id == ident) return vf.name;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**Return the file extension of the media format with the supplied id
|
|
||||||
* @param ident the id of the media format. Currently an arbitrary, NewPipe-specific number.
|
|
||||||
* @return the file extension of the MediaFormat associated with this ids,
|
|
||||||
* or an empty String if none match it.*/
|
|
||||||
public static String getSuffixById(int ident) {
|
|
||||||
for (MediaFormat vf : MediaFormat.values()) {
|
|
||||||
if(vf.id == ident) return vf.suffix;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**Return the MIME type of the media format with the supplied id
|
|
||||||
* @param ident the id of the media format. Currently an arbitrary, NewPipe-specific number.
|
|
||||||
* @return the MIME type of the MediaFormat associated with this ids,
|
|
||||||
* or an empty String if none match it.*/
|
|
||||||
public static String getMimeById(int ident) {
|
|
||||||
for (MediaFormat vf : MediaFormat.values()) {
|
|
||||||
if(vf.id == ident) return vf.mimeType;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,82 +0,0 @@
|
|||||||
package org.schabi.newpipe.extractor;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
|
||||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 23.08.15.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
|
|
||||||
* NewPipe.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**Provides access to the video streaming services supported by NewPipe.
|
|
||||||
* Currently only Youtube until the API becomes more stable.*/
|
|
||||||
|
|
||||||
@SuppressWarnings("ALL")
|
|
||||||
public class NewPipe {
|
|
||||||
|
|
||||||
private NewPipe() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String TAG = NewPipe.class.toString();
|
|
||||||
|
|
||||||
private static final StreamingService[] serviceList = {
|
|
||||||
new YoutubeService(0)
|
|
||||||
};
|
|
||||||
|
|
||||||
private static Downloader downloader = null;
|
|
||||||
|
|
||||||
public static StreamingService[] getServices() {
|
|
||||||
return serviceList;
|
|
||||||
}
|
|
||||||
public static StreamingService getService(int serviceId)throws ExtractionException {
|
|
||||||
for(StreamingService s : serviceList) {
|
|
||||||
if(s.getServiceId() == serviceId) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new ExtractionException("Service not known: " + Integer.toString(serviceId));
|
|
||||||
}
|
|
||||||
public static StreamingService getService(String serviceName) throws ExtractionException {
|
|
||||||
return serviceList[getIdOfService(serviceName)];
|
|
||||||
}
|
|
||||||
public static String getNameOfService(int id) {
|
|
||||||
try {
|
|
||||||
return getService(id).getServiceInfo().name;
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Service id not known");
|
|
||||||
e.printStackTrace();
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static int getIdOfService(String serviceName) throws ExtractionException {
|
|
||||||
for(int i = 0; i < serviceList.length; i++) {
|
|
||||||
if(serviceList[i].getServiceInfo().name.equals(serviceName)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new ExtractionException("Error: Service " + serviceName + " not known.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void init(Downloader d) {
|
|
||||||
downloader = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Downloader getDownloader() {
|
|
||||||
return downloader;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,69 +0,0 @@
|
|||||||
package org.schabi.newpipe.extractor;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Christian Schabesberger on 02.02.16.
|
|
||||||
*
|
|
||||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
|
||||||
* Parser.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** avoid using regex !!! */
|
|
||||||
public class Parser {
|
|
||||||
|
|
||||||
private Parser() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class RegexException extends ParsingException {
|
|
||||||
public RegexException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String matchGroup1(String pattern, String input) throws RegexException {
|
|
||||||
Pattern pat = Pattern.compile(pattern);
|
|
||||||
Matcher mat = pat.matcher(input);
|
|
||||||
boolean foundMatch = mat.find();
|
|
||||||
if (foundMatch) {
|
|
||||||
return mat.group(1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//Log.e(TAG, "failed to find pattern \""+pattern+"\" inside of \""+input+"\"");
|
|
||||||
throw new RegexException("failed to find pattern \""+pattern+" inside of "+input+"\"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<String, String> compatParseMap(final String input) throws UnsupportedEncodingException {
|
|
||||||
Map<String, String> map = new HashMap<>();
|
|
||||||
for(String arg : input.split("&")) {
|
|
||||||
String[] splitArg = arg.split("=");
|
|
||||||
if(splitArg.length > 1) {
|
|
||||||
map.put(splitArg[0], URLDecoder.decode(splitArg[1], "UTF-8"));
|
|
||||||
} else {
|
|
||||||
map.put(splitArg[0], "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user