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

Compare commits

...

137 Commits

Author SHA1 Message Date
Christian Schabesberger
f9ad0f12d0 Fixed serveral things:
* ugly workaround for the details_view_layout problem on older devices
* removed "display button on the left side" option since it's not nececeay anymore.
2015-12-15 22:53:29 +01:00
M2ck
63b16d925d Translated using Weblate (French)
Currently translated at 100.0% (47 of 47 strings)
2015-12-15 14:28:58 +01:00
Greg
53b9ffcbc8 Translated using Weblate (Russian)
Currently translated at 100.0% (47 of 47 strings)
2015-12-15 14:25:10 +01:00
Weblate
842079c928 Merge remote-tracking branch 'origin/master' 2015-12-15 11:57:58 +01:00
M2ck
b0bab07a15 Translated using Weblate (French)
Currently translated at 97.8% (46 of 47 strings)
2015-12-15 11:57:58 +01:00
chschtsch
4dbb12c65d update screenshots 2015-12-15 13:55:56 +03:00
chschtsch
db500e9791 fix layout issues & update screenshots 2015-12-15 13:45:59 +03:00
chschtsch
fd8f600fec update .travis.yml & move screenshots to separate folder 2015-12-15 13:04:51 +03:00
Luca Argentieri
2e2d7d02fb Translated using Weblate (Italian)
Currently translated at 100.0% (47 of 47 strings)
2015-12-14 23:03:17 +01:00
Luca Argentieri
d068cd7f75 Translated using Weblate (Italian)
Currently translated at 100% (0 of 0 strings)

Created new translation.
2015-12-14 22:38:11 +01:00
Christian Schabesberger
5a05ffcbdd Merge pull request #114 from chschtsch/cardview
Design update & refactoring
2015-12-14 20:24:22 +01:00
chschtsch
d8c7f50b39 minor layout fixes 2015-12-14 14:52:14 +03:00
chschtsch
988e6e1c82 update like buttons 2015-12-14 14:42:13 +03:00
chschtsch
f6af19444c update like buttons 2015-12-14 14:34:28 +03:00
chschtsch
0581e50e0c update viewlike buttons 2015-12-14 14:33:00 +03:00
chschtsch
a95da9a42d update views & dimens 2015-12-14 14:10:12 +03:00
chschtsch
be10b9750f mering with master 2015-12-14 13:44:15 +03:00
chschtsch
29a3cbc688 mering with master 2015-12-14 13:07:54 +03:00
chschtsch
4f57d3a201 merging with master 2015-12-14 13:01:34 +03:00
Christian Schabesberger
c9be1398b0 fixed green arrow layout error 2015-12-13 21:13:48 +01:00
halcyonest
30eef4db12 Translated using Weblate (Korean)
Currently translated at 100.0% (47 of 47 strings)
2015-12-13 13:51:55 +01:00
halcyonest
b932dbf514 Translated using Weblate (Korean)
Currently translated at 100% (0 of 0 strings)

Created new translation.
2015-12-13 13:40:17 +01:00
chschtsch
320ac82dea merging with master 2015-12-11 14:01:04 +03:00
Christian Schabesberger
af1b21db23 code_lint and moved on to v0.6.1 2015-12-09 23:17:29 +01:00
Christian Schabesberger
0e892ff60e code_lint 2015-12-09 22:31:24 +01:00
Christian Schabesberger
074963aee0 put progressbar on top of thumbnail and made it red 2015-12-09 22:14:22 +01:00
Christian Schabesberger
37d9be9095 put fab button on top of video thumbnail 2015-12-08 21:29:29 +01:00
Christian Schabesberger
78b95f67eb made content scroll over thumbnail 2015-12-02 21:20:52 +01:00
chschtsch
6c63841d0c update video detail layout 2015-12-02 21:55:57 +03:00
chschtsch
6ec2d91d91 fix card margin as well 2015-12-02 17:36:20 +03:00
chschtsch
3299b90c20 better padding for landscape card 2015-12-02 17:33:58 +03:00
chschtsch
7b6d6da9a6 improve card margin & edit its dimensions for landscape 2015-12-02 17:26:20 +03:00
chschtsch
7c7c61fc35 use singleLine instead of manually setting height 2015-12-02 17:18:40 +03:00
chschtsch
c5408fb6b8 finally make card layout look good 2015-12-02 17:13:01 +03:00
chschtsch
1c49102f67 update card layout 2015-12-02 17:00:37 +03:00
chschtsch
f9dd88c1cb remove divider from listview 2015-12-02 16:29:58 +03:00
chschtsch
9ed4a65fd2 move all dimensions do dimens.xml 2015-12-02 16:23:31 +03:00
chschtsch
10bebf8a89 update cards & update dimens structure 2015-12-02 15:27:19 +03:00
chschtsch
36260dac18 tryna add cardview 2015-12-02 13:08:43 +03:00
Christian Schabesberger
2e4e993967 removed unececeary commends 2015-12-02 00:19:47 +01:00
Christian Schabesberger
ef1bfe98f7 try to fix timeout error 2015-12-02 00:11:39 +01:00
Christian Schabesberger
2e123aa617 try to get emulator running part 2 2015-12-01 23:55:07 +01:00
Christian Schabesberger
cf2ef0f2a8 try to get emulator running 2015-12-01 23:48:41 +01:00
Christian Schabesberger
520f40d862 fiexed travis build error 2015-12-01 23:16:58 +01:00
Christian Schabesberger
856bdac84b Merge branch 'master' of github.com:theScrabi/NewPipe 2015-12-01 22:32:55 +01:00
Christian Schabesberger
deb7e38fcf fixed travis.yml error 2015-12-01 22:32:44 +01:00
Weblate
e5f47a4563 Merge remote-tracking branch 'origin/master' 2015-12-01 21:56:17 +01:00
Roland Sawicki
d1b465d8be Translated using Weblate (Polish)
Currently translated at 100.0% (47 of 47 strings)
2015-12-01 21:56:17 +01:00
Christian Schabesberger
5c73b2f324 trigger first CI pull 2015-12-01 21:35:18 +01:00
Christian Schabesberger
7d3e992b3f init CI 2015-12-01 21:31:10 +01:00
Christian Schabesberger
296640930e fixed some spelling errors 2015-12-01 18:50:57 +01:00
Christian Schabesberger
1c42735e3a fixed screenshot error -.- 2015-12-01 18:37:50 +01:00
Christian Schabesberger
0800bc1790 post edit README.md 2015-12-01 18:36:39 +01:00
Christian Schabesberger
bd76a12b90 update README.md and readded (still) nececeary assets 2015-12-01 18:23:06 +01:00
Christian Schabesberger
d3daea6383 Merge pull request #107 from chschtsch/refactor
Slightly update readme file + add screenshots
2015-12-01 17:52:57 +01:00
naofum
df1acd5413 Translated using Weblate (Japanese)
Currently translated at 100.0% (47 of 47 strings)
2015-12-01 14:38:28 +01:00
Greg
0d65cc09f6 Merge pull request #9 from chschtsch/master
.
2015-12-01 12:59:16 +03:00
Greg
892b082b9e Update README.md 2015-12-01 12:58:47 +03:00
Mladen Pejaković
9069ef1325 Translated using Weblate (Serbian)
Currently translated at 100.0% (47 of 47 strings)
2015-11-30 22:24:17 +01:00
Roland Sawicki
47cc493ab5 Translated using Weblate (Polish)
Currently translated at 100.0% (47 of 47 strings)
2015-11-30 22:08:09 +01:00
Greg
f43d7837f8 Merge pull request #8 from chschtsch/refactor
Refactor
2015-11-30 23:56:14 +03:00
Greg
91921ae672 Update README.md 2015-11-30 23:55:24 +03:00
Greg
eba6afc12b Merge pull request #7 from chschtsch/master
.
2015-11-30 23:52:52 +03:00
Greg
ff826a9eeb Update README.md 2015-11-30 23:49:13 +03:00
Greg
6c01a30af5 Merge pull request #5 from chschtsch/refactor
edit screenshots
2015-11-30 23:43:09 +03:00
Greg
53f8d09d31 Merge pull request #6 from chschtsch/master
.
2015-11-30 23:42:54 +03:00
chschtsch
ae191aaafe edit screenshots 2015-11-30 23:40:44 +03:00
Greg
d1694d563b Update README.md 2015-11-30 23:40:29 +03:00
Greg
75fadf79da Delete gruese_die_gema_original.png 2015-11-30 23:38:30 +03:00
Greg
23ce5a6b1e Merge pull request #4 from chschtsch/refactor
add screenshots and new icon
2015-11-30 23:36:29 +03:00
chschtsch
bb65e2b84d add screenshots and new icon 2015-11-30 23:32:10 +03:00
Greg
aa42b1d95a Update README.md 2015-11-30 23:08:28 +03:00
Greg
32d5a18198 Update README.md 2015-11-30 23:07:07 +03:00
Greg
63faefe9c3 Update README.md 2015-11-30 23:01:55 +03:00
syl0s
a90c49d030 Translated using Weblate (German)
Currently translated at 100.0% (47 of 47 strings)
2015-11-30 20:57:19 +01:00
Christian Schabesberger
b1ef3fa4df Translated using Weblate (German)
Currently translated at 100.0% (47 of 47 strings)
2015-11-30 20:55:15 +01:00
Greg
b8cf67cba9 Merge pull request #3 from theScrabi/master
.
2015-11-30 22:53:01 +03:00
Weblate
4780e10ade Merge remote-tracking branch 'origin/master' 2015-11-30 20:51:43 +01:00
Roland Sawicki
401e606fbc Translated using Weblate (Polish)
Currently translated at 95.9% (47 of 49 strings)
2015-11-30 20:51:43 +01:00
syl0s
6b4ef8f397 Translated using Weblate (German)
Currently translated at 100.0% (49 of 49 strings)
2015-11-30 20:51:41 +01:00
Greg
7c66d07779 Merge pull request #2 from theScrabi/master
pull from theScrabi/NewPipe
2015-11-30 22:51:20 +03:00
Christian Schabesberger
92aed0cc3a add link to slack group 2015-11-30 20:48:59 +01:00
Christian Schabesberger
457b08d3cc merge strings.xml collision 2015-11-30 20:00:28 +01:00
Christian Schabesberger
1e5f6fd2b8 merge code_lint 2015-11-30 19:47:21 +01:00
Christian Schabesberger
f6974e8315 code lint 2015-11-29 13:11:56 +01:00
Xenotium
2ce6313ac1 Forgot about quotes... 2015-11-29 11:49:47 +01:00
RACER
e98a113a59 Translated using Weblate (Japanese)
Currently translated at 100.0% (49 of 49 strings)
2015-11-28 15:16:15 +01:00
Mladen Pejaković
ba7bed9c2c Translated using Weblate (Serbian)
Currently translated at 100.0% (49 of 49 strings)
2015-11-27 23:47:01 +01:00
Xenotium
c9ea451c53 Make some strings non-translatable
Apart from app name, which at the moment is the same no matter what language, there are URLs here. They shouldn't reside here but make them (and the app name) at least "translatable=false".
2015-11-27 20:51:56 +01:00
Roland Sawicki
4261ff32c7 Translated using Weblate (Polish)
Currently translated at 100% (0 of 0 strings)

Created new translation.
2015-11-27 20:10:57 +01:00
Christian Schabesberger
cb4b20af45 use format strings now 2015-11-26 20:43:16 +01:00
Christian Schabesberger
b8a27adb93 removed unused resources 2015-11-26 20:22:45 +01:00
Christian Schabesberger
15b58128f4 fiexed lint layout suggestions 2015-11-26 20:16:23 +01:00
Christian Schabesberger
237282db28 refactored VideoPlayer theme 2015-11-26 19:54:13 +01:00
Christian Schabesberger
71bb59dbb8 update gradle/libs and use Perference.Editor.apply() instead of commit() 2015-11-26 19:36:14 +01:00
Christian Schabesberger
e41c46c075 made lint ignore that parentActivityName is not compatible to sdk 15 2015-11-26 19:28:50 +01:00
Christian Schabesberger
6ca9e52f2f made lint ignore some code and layout warnings 2015-11-26 19:11:31 +01:00
Christian Schabesberger
2afee89de3 added contentDescription for better accesebility 2015-11-26 18:47:36 +01:00
Christian Schabesberger
189bee3e44 named drawable folder drawable-nodpi and moved orig. gema image to assets 2015-11-26 18:37:40 +01:00
Christian Schabesberger
6b9a4d5e0a add ellipsis character to spanish translation 2015-11-26 18:32:06 +01:00
Christian Schabesberger
451e2b2182 removed hardcoded strings 2015-11-26 18:29:00 +01:00
Christian Schabesberger
f6ff41cfb4 removed obsolete translations 2015-11-26 18:26:28 +01:00
Christian Schabesberger
d6d144c927 renamed Extractor into VideoExtractor 2015-11-26 17:29:26 +01:00
Christian Schabesberger
7f86872139 Categorized settings & moved AbstractVideoInfo into service folder 2015-11-25 21:08:19 +01:00
Greg
5d28b2400f Merge pull request #1 from theScrabi/master
merge from theScrabi/NewPipe
2015-11-23 23:02:30 +03:00
Weblate
67324bfc80 Merge remote-tracking branch 'origin/master' 2015-11-22 13:24:52 +01:00
syl0s
3c340e7144 Translated using Weblate (German)
Currently translated at 97.8% (45 of 46 strings)
2015-11-22 13:24:51 +01:00
dostoi
c834405a92 Translated using Weblate (French)
Currently translated at 97.8% (45 of 46 strings)
2015-11-22 13:24:51 +01:00
Greg
4ee9e26847 Translated using Weblate (Dutch)
Currently translated at 100.0% (46 of 46 strings)
2015-11-22 13:24:50 +01:00
Adam Howard
94293ca9d9 Merge pull request #100 from theScrabi/refactor
+ Implemented timestamps
* renamed `VideoInfoItem` to `VideoPreviewInfo`
* Moved streaming service-related classes into their own, new package: "services"
+ Added javadoc to some classes and methods (where functionality is known well enough to explain)
- De-duplicated common fields between `VideoInfo` and `VideoPreviewInfo` by moving them into a common superclass: `AbstractVideoInfo`
- Removed 2 methods in `PlayVideoActivity` which only call `super()`, and therefore are unnecessary: `onResume()` and `onPostCreate(Bundle)`
+ Added `VideoInfo(AbstractVideoInfo)` constructor
    - to support converting `VideoPreviewInfo`s into `VideoInfo`s, to reuse scraped info (yet to be implemented)
* Made the Extractor class behave as a per-video object;
    - most method return values are video-specific, so it makes sense (to me) to have Extractor be stateful. 
    - The only stateless methods are getVideoUrl(), getVideoId() and loadDecryptionCode(String)
* Implemented a constructor for YoutubeExtractor, which performs all initialisation work, such as fetching `Jsoup.Document`, and `playerArgs:JSONObject`
2015-11-21 11:11:17 +00:00
Mladen Pejaković
b9cd9f8d35 Translated using Weblate (Serbian)
Currently translated at 100.0% (46 of 46 strings)
2015-11-20 18:45:03 +01:00
Adam Howard
812dd9282d Merge pull request #102 from chschtsch/refactor
fix bug with upload date + better way to parse it
2015-11-20 01:44:32 +00:00
chschtsch
fa1d386fcc cleanup 2015-11-20 00:52:43 +03:00
chschtsch
0392bf6a02 fix bug with upload date + better way to parse it 2015-11-20 00:49:52 +03:00
naofum
97f771ff50 Translated using Weblate (Japanese)
Currently translated at 100.0% (46 of 46 strings)
2015-11-19 16:20:26 +01:00
naofum
6adcc72a8a Translated using Weblate (Japanese)
Currently translated at 100% (0 of 0 strings)

Created new translation.
2015-11-19 16:17:28 +01:00
Adam Howard
2c11bd1889 fixed Bundle casting bug incurred during related videosdisplay 2015-11-19 14:40:35 +00:00
Adam Howard
23e0196fcc * fixed counterintuitive back button behaviour in PlayVideoActivity; see https://github.com/theScrabi/NewPipe/issues/99
* fixed a rarely-caused NullPointerException caused by a related video's view-count field being missing
2015-11-19 00:08:51 +00:00
Adam Howard
91f98c125e finished implementing timestamp, along with refactoring services
* added VideoInfo(AbstractVideoInfo) constructor, to support later implementation for reusing info scraped into VideoPreviewInfo, into VideoInfo
* Made the Extractor class behave as a per-video object;
    - most method return values are video-specific, so it makes sense (to me) to have Extractor be stateful.
    - The only stateless methods are getVideoUrl(), getVideoId() and loadDecryptionCode(String)
* Implemented a constructor for YoutubeExtractor, which performs all initialisation work
2015-11-17 22:51:27 +00:00
Adam Howard
7f01e9a4d9 Merge branch 'master' of github.com:theScrabi/NewPipe
translations
2015-11-16 23:45:42 +00:00
Adam Howard
320a4e2351 Refactoring:
* renamed VideoInfoItem to VideoPreviewInfo
* moved streaming service-related classes into their own, new package services
* added javadoc to some classes and methods (where functionality is known well enough to explain)
* De-duplicated common fields between VideoInfo and VideoPreviewInfo by moving them into a common superclass: AbstractVideoInfo
* Removed 2 methods which only call super(), and therefore are unnecessary: PlayVideoActivity.onResume() and PlayVideoActivity.onPostCreate(Bundle)
2015-11-16 23:32:00 +00:00
Weblate
0829ce51fc Merge remote-tracking branch 'origin/master' 2015-11-16 17:36:14 +01:00
Mladen Pejaković
97ec50c202 Translated using Weblate (Serbian)
Currently translated at 100.0% (46 of 46 strings)
2015-11-16 17:36:12 +01:00
Adam Howard
975a3e8103 Merge branch 'master' of github.com:theScrabi/NewPipe 2015-11-16 14:18:12 +00:00
Christian Schabesberger
658ef2ef26 Merge pull request #97 from chschtsch/master
Update design
2015-11-15 11:49:49 +01:00
chschtsch
bd1e531d7b update design 2015-11-14 21:04:14 +03:00
chschtsch
e440d1d1bd update design 2015-11-14 20:55:28 +03:00
chschtsch
2110020165 update design 2015-11-14 20:48:42 +03:00
chschtsch
dac74dc30d Merge github.com:chschtsch/NewPipe 2015-11-14 20:12:11 +03:00
chschtsch
fa4b971254 update .gitignore 2015-11-14 20:11:41 +03:00
Greg
2d31ae0baa Delete NewPipe.iml 2015-11-14 19:20:31 +03:00
chschtsch
5eebfa132f update .gitignore 2015-11-14 19:19:45 +03:00
chschtsch
522febef93 update .gitignore 2015-11-14 19:17:54 +03:00
chschtsch
a421645ea5 update fragment_videoitem_detail layout 2015-11-14 19:14:51 +03:00
chschtsch
0aac4b1347 update fragment_videoitem_detail layout 2015-11-14 19:11:08 +03:00
chschtsch
36697825cf update fragment_videoitem_detail layout 2015-11-14 19:08:13 +03:00
Adam Howard
b0182ed604 halfway through implementing timestamps:
* still need to use the start position value stored in the PlayVideoActivity's Intent bundle, to set the VideoView using seekTo()
* need to test timestamp extraction regex, and possibly move it somewhere else
*need to find a better way to get the startPosition value to ActionBarHandler, which I thought used VideoInfo objects, but apparently doesn't
    * currently there is a small setStartPosition() method
2015-11-14 11:47:21 +00:00
94 changed files with 1883 additions and 2323 deletions

1
.gitignore vendored
View File

@@ -6,3 +6,4 @@
/captures
/app/app.iml
/.idea
/*.iml

29
.travis.yml Normal file
View File

@@ -0,0 +1,29 @@
language: android
android:
components:
# The BuildTools version used by NewPipe
- build-tools-23.0.2
# The SDK version used to compile NewPipe
- android-23
# Additional components
- extra-android-support
- extra-android-m2repository
# Emulators
- 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)
matrix:
- ANDROID_TARGET=android-19 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 &

View File

@@ -1,30 +1,43 @@
# NewPipe
NewPipe: A free lightweight Youtube frontend for Android.
[![NewPipe](app/src/main/res/mipmap-xhdpi/ic_launcher.png)](http://dasochan.nl/newpipe/)
Project status:
[![Translation Status](https://hosted.weblate.org/widgets/NewPipe/-/svg-badge.svg)](https://hosted.weblate.org/engage/NewPipe/)
[![Build Status](https://travis-ci.org/theScrabi/NewPipe.svg)](https://travis-ci.org/theScrabi/NewPipe)
[![NewPipe](https://f-droid.org/repo/icons/org.schabi.newpipe.5.png)](http://dasochan.nl/newpipe/)
NewPipe: A free lightweight Youtube frontend for Android.
## Get NewPipe
[![F-Droid](https://f-droid.org/wiki/images/0/06/F-Droid-button_get-it-on.png)](https://f-droid.org/repository/browse/?fdfilter=newpipe&fdid=org.schabi.newpipe)
## Screenshots
[<img src="screenshots/screenshot_1.png" width=150>](screenshots/screenshot_1.png)
[<img src="screenshots/screenshot_2.png" width=150>](screenshots/screenshot_2.png)
[<img src="screenshots/screenshot_3.png" width=150>](screenshots/screenshot_3.png)
[<img src="screenshots/screenshot_4.png" width=150>](screenshots/screenshot_4.png)
[<img src="screenshots/screenshot_5.png" width=150>](screenshots/screenshot_5.png)
[<img src="screenshots/screenshot_6.png" width=250>](screenshots/screenshot_6.png)
## Description
NewPipe does not use any Google framework libraries, or the YouTube API. It only parses the website in order to gain the information it needs. Therefore this app can be used on devices without G-services installed. Also NewPipe does not store data on the YouTube website (no login), and it's free software.
NewPipe does not use any Google framework libraries, or the YouTube API. It only parses the website in order to gain the information it needs. Therefore this app can be used on devices without Google Services installed. Also, you don't need a YouTube account to use NewPipe, and it's FLOSS.
## Features
### Features
* Search videos
* Display general information about a video
* Watch Youtube videos
* Listen to Youtube videos (audio only streaming)
* Watch YouTube videos
* Listen to YouTube videos (audio only streaming)
* Select the streaming player to watch the video with
* Download videos (working, but it could be better)
* Download audio only (working but, but it could be better)
* Download audio only (working, but it could be better)
* Open a video in Kodi
* Show Next/Related videos
* Search Youtube in a specific language
* Search YouTube in a specific language
## Coming Features
### Coming Features
* Improved Downloading
* Bookmarks
@@ -37,10 +50,20 @@ NewPipe does not use any Google framework libraries, or the YouTube API. It only
* Search/Watch Playlists
* ... and many more
### Multi service support
Generally NewPipe is designed to not only support YouTube, but many more streaming services. However, right now NewPipe is not stable enough to support more than only YouTube. But if all works as planned, NewPipe will get such support by the version 2.0.
# Help is always welcome !!!
Whether it's about ideas, translation, design changes, code cleaning, or real heavy code changes. Help is always welcome.
### Multiservice support
Although NewPipe only supports YouTube at the moment, it's designed to support many more streaming services. The plan is, that NewPipe will get such support by the version 2.0.
## Contribution
Whether you have ideas, translation, design changes, code cleaning, or real heavy code changes, help is always welcome.
The more is done the better it gets!
Join our [Slack group](http://invite.chschtsch.ml/) if you like to get involved.
## License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html)
NewPipe is Free Software: You can use, study share and improve it at your
will. Specifically you can redistribute and/or modify it under the terms of the
[GNU General Public License](https://www.gnu.org/licenses/gpl.html) as
published by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

View File

@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "org.schabi.newpipe"
minSdkVersion 15
targetSdkVersion 23
versionCode 6
versionName "0.6.0"
versionCode 8
versionName "0.6.2"
}
buildTypes {
release {
@@ -17,13 +17,22 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
checkReleaseBuilds false
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:
abortOnError false
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:support-v4:23.1.0'
compile 'com.android.support:design:23.1.0'
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:support-v4:23.1.1'
compile 'com.android.support:design:23.1.1'
compile 'com.android.support:cardview-v7:23.1.1'
compile 'com.android.support:recyclerview-v7:23.1.1'
compile 'org.jsoup:jsoup:1.8.3'
compile 'org.mozilla:rhino:1.7.7'
}

View File

@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.schabi.newpipe" >
<uses-permission android:name= "android.permission.INTERNET" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
@@ -10,10 +11,11 @@
android:icon="@mipmap/ic_launcher"
android:logo="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
android:theme="@style/AppTheme"
tools:ignore="AllowBackup">
<activity
android:name=".VideoItemListActivity"
android:label="@string/app_name" >
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -22,7 +24,8 @@
</activity>
<activity
android:name=".VideoItemDetailActivity"
android:label="@string/title_videoitem_detail" >
android:label="@string/title_videoitem_detail"
android:theme="@style/AppTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".VideoItemListActivity" />
@@ -67,9 +70,9 @@
</activity>
<activity android:name=".PlayVideoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/FullscreenTheme"
android:theme="@style/VideoPlayerTheme"
android:parentActivityName=".VideoItemDetailActivity"
>
tools:ignore="UnusedAttribute">
</activity>
<activity
android:name=".SettingsActivity"

View File

@@ -6,7 +6,6 @@ import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
@@ -36,7 +35,8 @@ import android.widget.ArrayAdapter;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class ActionBarHandler {
class ActionBarHandler {
private static final String TAG = ActionBarHandler.class.toString();
private static final String KORE_PACKET = "org.xbmc.kore";
@@ -47,9 +47,11 @@ public class ActionBarHandler {
private int selectedStream = -1;
private String videoTitle = "";
SharedPreferences defaultPreferences = null;
private SharedPreferences defaultPreferences = null;
private int startPosition;
class FormatItemSelectListener implements ActionBar.OnNavigationListener {
@SuppressWarnings("deprecation")
private class FormatItemSelectListener implements ActionBar.OnNavigationListener {
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
selectFormatItem((int)itemId);
@@ -61,11 +63,17 @@ public class ActionBarHandler {
this.activity = activity;
}
@SuppressWarnings({"deprecation", "ConstantConditions"})
public void setupNavMenu(AppCompatActivity activity) {
this.activity = activity;
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
try {
activity.getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
} catch(NullPointerException e) {
e.printStackTrace();
}
}
@SuppressWarnings("deprecation")
public void setStreams(VideoInfo.VideoStream[] videoStreams, VideoInfo.AudioStream[] audioStreams) {
this.videoStreams = videoStreams;
selectedStream = 0;
@@ -83,12 +91,14 @@ public class ActionBarHandler {
}
}
ArrayAdapter<String> itemAdapter = new ArrayAdapter<String>(activity.getBaseContext(),
ArrayAdapter<String> itemAdapter = new ArrayAdapter<>(activity.getBaseContext(),
android.R.layout.simple_spinner_dropdown_item, itemArray);
if(activity != null) {
ActionBar ab = activity.getSupportActionBar();
ab.setListNavigationCallbacks(itemAdapter
,new FormatItemSelectListener());
assert ab != null : "Could not get actionbar";
ab.setListNavigationCallbacks(itemAdapter
, new FormatItemSelectListener());
ab.setSelectedNavigationItem(defaultResolutionPos);
}
@@ -116,7 +126,7 @@ public class ActionBarHandler {
selectedStream = i;
}
public boolean setupMenu(Menu menu, MenuInflater inflater) {
public void setupMenu(Menu menu, MenuInflater inflater) {
// CAUTION set item properties programmatically otherwise it would not be accepted by
// appcompat itemsinflater.inflate(R.menu.videoitem_detail, menu);
@@ -127,8 +137,6 @@ public class ActionBarHandler {
castItem.setVisible(defaultPreferences
.getBoolean(activity.getString(R.string.showPlayWidthKodiPreference), false));
return true;
}
public boolean onItemSelected(MenuItem item) {
@@ -216,13 +224,19 @@ public class ActionBarHandler {
intent.putExtra(PlayVideoActivity.VIDEO_TITLE, videoTitle);
intent.putExtra(PlayVideoActivity.STREAM_URL, videoStreams[selectedStream].url);
intent.putExtra(PlayVideoActivity.VIDEO_URL, websiteUrl);
activity.startActivity(intent);
intent.putExtra(PlayVideoActivity.START_POSITION, startPosition);
activity.startActivity(intent); //also HERE !!!
}
}
// --------------------------------------------
}
public void downloadVideo() {
public void setStartPosition(int startPositionSeconds)
{
this.startPosition = startPositionSeconds;
}
private void downloadVideo() {
if(!videoTitle.isEmpty()) {
String videoSuffix = "." + MediaFormat.getSuffixById(videoStreams[selectedStream].format);
String audioSuffix = "." + MediaFormat.getSuffixById(audioStream.format);
@@ -238,7 +252,7 @@ public class ActionBarHandler {
}
}
public void openInBrowser() {
private void openInBrowser() {
if(!videoTitle.isEmpty()) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
@@ -248,7 +262,7 @@ public class ActionBarHandler {
}
}
public void playWithKodi() {
private void playWithKodi() {
if(!videoTitle.isEmpty()) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
@@ -279,7 +293,7 @@ public class ActionBarHandler {
}
}
public void playAudio() {
private void playAudio() {
Intent intent = new Intent();
try {
intent.setAction(Intent.ACTION_VIEW);

View File

@@ -8,6 +8,7 @@ import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.util.Log;
@@ -42,8 +43,9 @@ public class DownloadDialog extends DialogFragment {
public static final String FILE_SUFFIX_VIDEO = "file_suffix_video";
public static final String AUDIO_URL = "audio_url";
public static final String VIDEO_URL = "video_url";
Bundle arguments;
private Bundle arguments;
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
arguments = getArguments();

View File

@@ -31,6 +31,11 @@ public class Downloader {
private static final String USER_AGENT = "Mozilla/5.0";
/**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*/
public static String download(String siteUrl, String language) {
String ret = "";
try {
@@ -44,9 +49,9 @@ public class Downloader {
}
return ret;
}
private static String dl(HttpURLConnection con) {
StringBuffer response = new StringBuffer();
/**Common functionality between download(String url) and download(String url, String language)*/
private static String dl(HttpURLConnection con) throws IOException {
StringBuilder response = new StringBuilder();
try {
con.setRequestMethod("GET");
@@ -66,13 +71,14 @@ public class Downloader {
uhe.printStackTrace();
//Toast.makeText(getActivity(), uhe.getMessage(), Toast.LENGTH_LONG).show();
}
catch (Exception e) {
e.printStackTrace();
}
return response.toString();
}
/**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*/
public static String download(String siteUrl) {
String ret = "";

View File

@@ -1,28 +0,0 @@
package org.schabi.newpipe;
/**
* Created by Christian Schabesberger on 10.08.15.
*
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
* Extractor.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 Extractor {
VideoInfo getVideoInfo(String siteUrl);
String getVideoUrl(String videoId);
String getVideoId(String videoUrl);
}

View File

@@ -21,6 +21,9 @@ package org.schabi.newpipe;
* 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 {
// id name suffix mime type
MPEG_4 (0x0, "MPEG-4", "mp4", "video/mp4"),
@@ -30,7 +33,9 @@ public enum MediaFormat {
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;
@@ -41,6 +46,10 @@ public enum MediaFormat {
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;
@@ -48,6 +57,10 @@ public enum MediaFormat {
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;
@@ -55,6 +68,10 @@ public enum MediaFormat {
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;

View File

@@ -15,6 +15,7 @@ import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -52,9 +53,9 @@ public class PlayVideoActivity extends AppCompatActivity {
public static final String STREAM_URL = "stream_url";
public static final String VIDEO_TITLE = "video_title";
private static final String POSITION = "position";
public static final String START_POSITION = "start_position";
private static final long HIDING_DELAY = 3000;
private static final long TAB_HIDING_DELAY = 100;
private String videoUrl = "";
@@ -82,12 +83,38 @@ public class PlayVideoActivity extends AppCompatActivity {
hasSoftKeys = checkIfHasSoftKeys();
actionBar = getSupportActionBar();
assert actionBar != null;
actionBar.setDisplayHomeAsUpEnabled(true);
Intent intent = getIntent();
if(mediaController == null) {
mediaController = new MediaController(this);
//prevents back button hiding media controller controls (after showing them)
//instead of exiting video
//see http://stackoverflow.com/questions/6051825
//also solves https://github.com/theScrabi/NewPipe/issues/99
mediaController = new MediaController(this) {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
int keyCode = event.getKeyCode();
final boolean uniqueDown = event.getRepeatCount() == 0
&& event.getAction() == KeyEvent.ACTION_DOWN;
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (uniqueDown)
{
if (isShowing()) {
finish();
} else {
hide();
}
}
return true;
}
return super.dispatchKeyEvent(event);
}
};
}
position = intent.getIntExtra(START_POSITION, 0)*1000;//convert from seconds to milliseconds
videoView = (VideoView) findViewById(R.id.video_view);
progressBar = (ProgressBar) findViewById(R.id.play_video_progress_bar);
try {
@@ -145,11 +172,6 @@ public class PlayVideoActivity extends AppCompatActivity {
}
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
}
@Override
public boolean onCreatePanelMenu(int featured, Menu menu) {
super.onCreatePanelMenu(featured, menu);
@@ -159,11 +181,6 @@ public class PlayVideoActivity extends AppCompatActivity {
return true;
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
@@ -274,11 +291,9 @@ public class PlayVideoActivity extends AppCompatActivity {
}
private boolean checkIfHasSoftKeys(){
if(Build.VERSION.SDK_INT >= 17) {
return getNavigationBarHeight() != 0 || getNavigationBarWidth() != 0;
} else {
return true;
}
return Build.VERSION.SDK_INT >= 17 ||
getNavigationBarHeight() != 0 ||
getNavigationBarWidth() != 0;
}
private int getNavigationBarHeight() {
@@ -315,7 +330,7 @@ public class PlayVideoActivity extends AppCompatActivity {
}
}
public boolean checkIfLandscape() {
private boolean checkIfLandscape() {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.heightPixels < displayMetrics.widthPixels;
@@ -331,6 +346,6 @@ public class PlayVideoActivity extends AppCompatActivity {
}
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(PREF_IS_LANDSCAPE, isLandscape);
editor.commit();
editor.apply();
}
}

View File

@@ -9,10 +9,9 @@ import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
@@ -70,14 +69,11 @@ public class SettingsActivity extends PreferenceActivity {
getDelegate().onPostCreate(savedInstanceState);
}
public ActionBar getSupportActionBar() {
private ActionBar getSupportActionBar() {
return getDelegate().getSupportActionBar();
}
public void setSupportActionBar(@Nullable Toolbar toolbar) {
getDelegate().setSupportActionBar(toolbar);
}
@NonNull
@Override
public MenuInflater getMenuInflater() {
return getDelegate().getMenuInflater();
@@ -162,7 +158,7 @@ public class SettingsActivity extends PreferenceActivity {
Environment.getExternalStorageDirectory().getAbsolutePath() + "/NewPipe";
spEditor.putString(context.getString(R.string.downloadPathPreference)
, newPipeDownloadStorage);
spEditor.commit();
spEditor.apply();
}
}
}

View File

@@ -1,10 +1,8 @@
package org.schabi.newpipe;
import android.graphics.Bitmap;
import android.util.Log;
import org.schabi.newpipe.services.AbstractVideoInfo;
import java.util.Date;
import java.util.Vector;
import java.util.List;
/**
* Created by Christian Schabesberger on 26.08.15.
@@ -27,53 +25,77 @@ import java.util.Vector;
*/
/**Info object for opened videos, ie the video ready to play.*/
public class VideoInfo {
public String id = "";
public String title = "";
public String uploader = "";
public String thumbnail_url = "";
public Bitmap thumbnail = null;
public String webpage_url = "";
public String upload_date = "";
public long view_count = 0;
@SuppressWarnings("ALL")
public class VideoInfo extends AbstractVideoInfo {
public String uploader_thumbnail_url = "";
public Bitmap uploader_thumbnail = null;
public String description = "";
public int duration = -1;
public int age_limit = 0;
public int like_count = 0;
public int dislike_count = 0;
public String average_rating = "";
public VideoStream[] videoStreams = null;
public AudioStream[] audioStreams = null;
public VideoInfoItem nextVideo = null;
public VideoInfoItem[] relatedVideos = null;
public int videoAvailableStatus = VIDEO_AVAILABLE;
public int duration = -1;
private static final String TAG = VideoInfo.class.toString();
/*YouTube-specific fields
todo: move these to a subclass*/
public int age_limit = 0;
public int like_count = -1;
public int dislike_count = -1;
public String average_rating = "";
public VideoPreviewInfo nextVideo = null;
public List<VideoPreviewInfo> relatedVideos = null;
public int startPosition = -1;//in seconds. some metadata is not passed using a VideoInfo object!
public static final int VIDEO_AVAILABLE = 0x00;
public static final int VIDEO_UNAVAILABLE = 0x01;
public static final int VIDEO_UNAVAILABLE_GEMA = 0x02;//German DRM organisation
public static class VideoStream {
public VideoStream(String url, int format, String res) {
this.url = url; this.format = format; resolution = res;
public VideoInfo() {}
/**Creates a new VideoInfo object from an existing AbstractVideoInfo.
* All the shared properties are copied to the new VideoInfo.*/
@SuppressWarnings("WeakerAccess")
public VideoInfo(AbstractVideoInfo avi) {
this.id = avi.id;
this.title = avi.title;
this.uploader = avi.uploader;
this.thumbnail_url = avi.thumbnail_url;
this.thumbnail = avi.thumbnail;
this.webpage_url = avi.webpage_url;
this.upload_date = avi.upload_date;
this.upload_date = avi.upload_date;
this.view_count = avi.view_count;
//todo: better than this
if(avi instanceof VideoPreviewInfo) {//shitty String to convert code
String dur = ((VideoPreviewInfo)avi).duration;
int minutes = Integer.parseInt(dur.substring(0, dur.indexOf(":")));
int seconds = Integer.parseInt(dur.substring(dur.indexOf(":")+1, dur.length()));
this.duration = (minutes*60)+seconds;
}
}
public static class VideoStream {
public String url = ""; //url of the stream
public int format = -1;
public String resolution = "";
public VideoStream(String url, int format, String res) {
this.url = url; this.format = format; resolution = res;
}
}
@SuppressWarnings("unused")
public static class AudioStream {
public AudioStream(String url, int format, int bandwidth, int samplingRate) {
this.url = url; this.format = format;
this.bandwidth = bandwidth; this.samplingRate = samplingRate;
}
public String url = "";
public int format = -1;
public int bandwidth = -1;
public int samplingRate = -1;
public AudioStream(String url, int format, int bandwidth, int samplingRate) {
this.url = url; this.format = format;
this.bandwidth = bandwidth; this.samplingRate = samplingRate;
}
}
}

View File

@@ -26,16 +26,14 @@ import android.widget.TextView;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class VideoInfoItemViewCreator {
private static final String TAG = VideoInfoItemViewCreator.class.toString();
LayoutInflater inflater;
class VideoInfoItemViewCreator {
private final LayoutInflater inflater;
public VideoInfoItemViewCreator(LayoutInflater inflater) {
this.inflater = inflater;
}
public View getViewByVideoInfoItem(View convertView, ViewGroup parent, VideoInfoItem info) {
public View getViewByVideoInfoItem(View convertView, ViewGroup parent, VideoPreviewInfo info) {
ViewHolder holder;
if(convertView == null) {
convertView = inflater.inflate(R.layout.video_item, parent, false);
@@ -62,7 +60,7 @@ public class VideoInfoItemViewCreator {
holder.itemUploadDateView.setText(info.upload_date);
} else {
//tweak if necessary: This is a hack to prevent having white space in the layout :P
holder.itemUploadDateView.setText(info.view_count);
holder.itemUploadDateView.setText(String.format("%d", info.view_count));
}
return convertView;

View File

@@ -7,10 +7,12 @@ import android.support.v4.app.NavUtils;
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.Toast;
import org.schabi.newpipe.services.ServiceList;
import org.schabi.newpipe.services.StreamingService;
/**
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
@@ -34,7 +36,7 @@ public class VideoItemDetailActivity extends AppCompatActivity {
private static final String TAG = VideoItemDetailActivity.class.toString();
VideoItemDetailFragment fragment;
private VideoItemDetailFragment fragment;
private String videoUrl;
private int currentStreamingService = -1;
@@ -44,7 +46,13 @@ public class VideoItemDetailActivity extends AppCompatActivity {
setContentView(R.layout.activity_videoitem_detail);
// Show the Up button in the action bar.
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
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
@@ -61,27 +69,24 @@ public class VideoItemDetailActivity extends AppCompatActivity {
// this means the video was called though another app
if (getIntent().getData() != null) {
videoUrl = getIntent().getData().toString();
Log.i(TAG, "video URL passed:\"" + videoUrl + "\"");
StreamingService[] serviceList = ServiceList.getServices();
Extractor extractor = null;
//VideoExtractor videoExtractor = null;
for (int i = 0; i < serviceList.length; i++) {
if (serviceList[i].acceptUrl(videoUrl)) {
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, i);
try {
currentStreamingService = i;
extractor = ServiceList.getService(i).getExtractorInstance();
} catch (Exception e) {
e.printStackTrace();
}
currentStreamingService = i;
//videoExtractor = ServiceList.getService(i).getExtractorInstance();
break;
}
}
if(extractor == null) {
if(currentStreamingService == -1) {
Toast.makeText(this, R.string.urlNotSupportedText, Toast.LENGTH_LONG)
.show();
}
arguments.putString(VideoItemDetailFragment.VIDEO_URL,
extractor.getVideoUrl(extractor.getVideoId(videoUrl)));
//arguments.putString(VideoItemDetailFragment.VIDEO_URL,
// videoExtractor.getVideoUrl(videoExtractor.getVideoId(videoUrl)));//cleans URL
arguments.putString(VideoItemDetailFragment.VIDEO_URL, videoUrl);
arguments.putBoolean(VideoItemDetailFragment.AUTO_PLAY,
PreferenceManager.getDefaultSharedPreferences(this)
.getBoolean(getString(R.string.autoPlayThroughIntent), false));

View File

@@ -3,8 +3,6 @@ package org.schabi.newpipe;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.app.NavUtils;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
@@ -14,10 +12,10 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.Arrays;
import org.schabi.newpipe.services.ServiceList;
/**
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
@@ -59,9 +57,9 @@ public class VideoItemListActivity extends AppCompatActivity
private VideoItemListFragment listFragment;
private VideoItemDetailFragment videoFragment = null;
Menu menu = null;
private Menu menu = null;
public class SearchVideoQueryListener implements SearchView.OnQueryTextListener {
private class SearchVideoQueryListener implements SearchView.OnQueryTextListener {
@Override
public boolean onQueryTextSubmit(String query) {
@@ -72,8 +70,14 @@ public class VideoItemListActivity extends AppCompatActivity
// hide virtual keyboard
InputMethodManager inputManager =
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(
getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
try {
//noinspection ConstantConditions
inputManager.hideSoftInputFromWindow(
getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
} catch(NullPointerException e) {
Log.e(TAG, "Could not get widget with focus");
e.printStackTrace();
}
// clear focus
// 1. to not open up the keyboard after switching back to this
// 2. It's a workaround to a seeming bug by the Android OS it self, causing
@@ -116,10 +120,16 @@ public class VideoItemListActivity extends AppCompatActivity
if(arguments != null) {
//Parcelable[] p = arguments.getParcelableArray(VIDEO_INFO_ITEMS);
ArrayList<VideoInfoItem> p = arguments.getParcelableArrayList(VIDEO_INFO_ITEMS);
ArrayList<VideoPreviewInfo> p = arguments.getParcelableArrayList(VIDEO_INFO_ITEMS);
if(p != null) {
mode = PRESENT_VIDEOS_MODE;
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
try {
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} catch (NullPointerException e) {
Log.e(TAG, "Could not get SupportActionBar");
e.printStackTrace();
}
listFragment.present(p);
}

View File

@@ -15,10 +15,12 @@ import android.widget.ListView;
import android.widget.Toast;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import org.schabi.newpipe.services.SearchEngine;
import org.schabi.newpipe.services.StreamingService;
/**
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
@@ -62,8 +64,8 @@ public class VideoItemListFragment extends ListFragment {
private ListView list;
private class ResultRunnable implements Runnable {
private SearchEngine.Result result;
private int requestId;
private final SearchEngine.Result result;
private final int requestId;
public ResultRunnable(SearchEngine.Result result, int requestId) {
this.result = result;
this.requestId = requestId;
@@ -75,12 +77,12 @@ public class VideoItemListFragment extends ListFragment {
}
private class SearchRunnable implements Runnable {
private SearchEngine engine;
private String query;
private int page;
Handler h = new Handler();
private final SearchEngine engine;
private final String query;
private final int page;
final Handler h = new Handler();
private volatile boolean run = true;
private int requestId;
private final int requestId;
public SearchRunnable(SearchEngine engine, String query, int page, int requestId) {
this.engine = engine;
this.query = query;
@@ -114,14 +116,14 @@ public class VideoItemListFragment extends ListFragment {
}
private class LoadThumbsRunnable implements Runnable {
private Vector<String> thumbnailUrlList = new Vector<>();
private Vector<Boolean> downloadedList;
Handler h = new Handler();
private final Vector<String> thumbnailUrlList = new Vector<>();
private final Vector<Boolean> downloadedList;
final Handler h = new Handler();
private volatile boolean run = true;
private int requestId;
public LoadThumbsRunnable(Vector<VideoInfoItem> videoList,
private final int requestId;
public LoadThumbsRunnable(Vector<VideoPreviewInfo> videoList,
Vector<Boolean> downloadedList, int requestId) {
for(VideoInfoItem item : videoList) {
for(VideoPreviewInfo item : videoList) {
thumbnailUrlList.add(item.thumbnail_url);
}
this.downloadedList = downloadedList;
@@ -137,7 +139,7 @@ public class VideoItemListFragment extends ListFragment {
public void run() {
for(int i = 0; i < thumbnailUrlList.size() && run; i++) {
if(!downloadedList.get(i)) {
Bitmap thumbnail = null;
Bitmap thumbnail;
try {
thumbnail = BitmapFactory.decodeStream(
new URL(thumbnailUrlList.get(i)).openConnection().getInputStream());
@@ -151,9 +153,9 @@ public class VideoItemListFragment extends ListFragment {
}
private class SetThumbnailRunnable implements Runnable {
private int index;
private Bitmap thumbnail;
private int requestId;
private final int index;
private final Bitmap thumbnail;
private final int requestId;
public SetThumbnailRunnable(int index, Bitmap thumbnail, int requestId) {
this.index = index;
this.thumbnail = thumbnail;
@@ -162,13 +164,13 @@ public class VideoItemListFragment extends ListFragment {
@Override
public void run() {
if(requestId == currentRequestId) {
videoListAdapter.updateDownloadedThumbnailList(index, true);
videoListAdapter.updateDownloadedThumbnailList(index);
videoListAdapter.setThumbnail(index, thumbnail);
}
}
}
public void present(List<VideoInfoItem> videoList) {
public void present(List<VideoPreviewInfo> videoList) {
mode = PRESENT_VIDEOS_MODE;
setListShown(true);
getListView().smoothScrollToPosition(0);
@@ -186,7 +188,7 @@ public class VideoItemListFragment extends ListFragment {
getListView().smoothScrollToPosition(0);
}
public void nextPage() {
private void nextPage() {
lastPage++;
Log.d(TAG, getString(R.string.searchPage) + Integer.toString(lastPage));
startSearch(query, lastPage);
@@ -205,7 +207,7 @@ public class VideoItemListFragment extends ListFragment {
this.streamingService = streamingService;
}
public void updateListOnResult(SearchEngine.Result result, int requestId) {
private void updateListOnResult(SearchEngine.Result result, int requestId) {
if(requestId == currentRequestId) {
setListShown(true);
if (result.resultList.isEmpty()) {
@@ -220,7 +222,7 @@ public class VideoItemListFragment extends ListFragment {
}
}
private void updateList(List<VideoInfoItem> list) {
private void updateList(List<VideoPreviewInfo> list) {
try {
videoListAdapter.addVideoList(list);
terminateThreads();
@@ -235,7 +237,7 @@ public class VideoItemListFragment extends ListFragment {
}
}
public void terminateThreads() {
private void terminateThreads() {
if(loadThumbsRunnable != null && loadThumbsRunnable.isRunning()) {
loadThumbsRunnable.terminate();
try {
@@ -274,12 +276,7 @@ public class VideoItemListFragment extends ListFragment {
void onItemSelected(String id);
}
Callbacks mCallbacks = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
private Callbacks mCallbacks = null;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
@@ -331,11 +328,6 @@ public class VideoItemListFragment extends ListFragment {
mCallbacks = (Callbacks) context;
}
@Override
public void onDetach() {
super.onDetach();
}
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
@@ -343,22 +335,11 @@ public class VideoItemListFragment extends ListFragment {
mCallbacks.onItemSelected(Long.toString(id));
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/*
if (mActivatedPosition != ListView.INVALID_POSITION) {
// Serialize and persist the activated item position.
outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
}
*/
}
/**
* Turns on activate-on-click mode. When this mode is on, list items will be
* given the 'activated' state when touched.
*/
public void setActivateOnItemClick(boolean activateOnItemClick) {
public void setActivateOnItemClick(@SuppressWarnings("SameParameterValue") boolean activateOnItemClick) {
// When setting CHOICE_MODE_SINGLE, ListView will automatically
// give items the 'activated' state when touched.
getListView().setChoiceMode(activateOnItemClick

View File

@@ -32,24 +32,22 @@ import java.util.Vector;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public class VideoListAdapter extends BaseAdapter {
private static final String TAG = VideoListAdapter.class.toString();
private Context context;
private VideoInfoItemViewCreator viewCreator;
private Vector<VideoInfoItem> videoList = new Vector<>();
class VideoListAdapter extends BaseAdapter {
private final Context context;
private final VideoInfoItemViewCreator viewCreator;
private Vector<VideoPreviewInfo> videoList = new Vector<>();
private Vector<Boolean> downloadedThumbnailList = new Vector<>();
VideoItemListFragment videoListFragment;
ListView listView;
private final ListView listView;
public VideoListAdapter(Context context, VideoItemListFragment videoListFragment) {
viewCreator = new VideoInfoItemViewCreator(LayoutInflater.from(context));
this.videoListFragment = videoListFragment;
this.listView = videoListFragment.getListView();
this.listView.setDivider(null);
this.listView.setDividerHeight(0);
this.context = context;
}
public void addVideoList(List<VideoInfoItem> videos) {
public void addVideoList(List<VideoPreviewInfo> videos) {
videoList.addAll(videos);
for(int i = 0; i < videos.size(); i++) {
downloadedThumbnailList.add(false);
@@ -63,12 +61,12 @@ public class VideoListAdapter extends BaseAdapter {
notifyDataSetChanged();
}
public Vector<VideoInfoItem> getVideoList() {
public Vector<VideoPreviewInfo> getVideoList() {
return videoList;
}
public void updateDownloadedThumbnailList(int index, boolean val) {
downloadedThumbnailList.set(index, val);
public void updateDownloadedThumbnailList(int index) {
downloadedThumbnailList.set(index, true);
}
public Vector<Boolean> getDownloadedThumbnailList() {

View File

@@ -4,11 +4,13 @@ import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
import org.schabi.newpipe.services.AbstractVideoInfo;
/**
* Created by Christian Schabesberger on 26.08.15.
*
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
* VideoInfoItem.java is part of NewPipe.
* VideoPreviewInfo.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
@@ -25,19 +27,10 @@ import android.os.Parcelable;
*/
/**Info object for previews of unopened videos, eg search results, related videos*/
public class VideoInfoItem implements Parcelable {
public String id = "";
public String title = "";
public String uploader = "";
public String thumbnail_url = "";
public Bitmap thumbnail = null;
public String webpage_url = "";
public String upload_date = "";
public String view_count = "";
public class VideoPreviewInfo extends AbstractVideoInfo implements Parcelable {
public String duration = "";
protected VideoInfoItem(Parcel in) {
@SuppressWarnings("WeakerAccess")
protected VideoPreviewInfo(Parcel in) {
id = in.readString();
title = in.readString();
uploader = in.readString();
@@ -46,10 +39,10 @@ public class VideoInfoItem implements Parcelable {
thumbnail = (Bitmap) in.readValue(Bitmap.class.getClassLoader());
webpage_url = in.readString();
upload_date = in.readString();
view_count = in.readString();
view_count = in.readLong();
}
public VideoInfoItem() {
public VideoPreviewInfo() {
}
@@ -68,19 +61,19 @@ public class VideoInfoItem implements Parcelable {
dest.writeValue(thumbnail);
dest.writeString(webpage_url);
dest.writeString(upload_date);
dest.writeString(view_count);
dest.writeLong(view_count);
}
@SuppressWarnings("unused")
public static final Parcelable.Creator<VideoInfoItem> CREATOR = new Parcelable.Creator<VideoInfoItem>() {
public static final Parcelable.Creator<VideoPreviewInfo> CREATOR = new Parcelable.Creator<VideoPreviewInfo>() {
@Override
public VideoInfoItem createFromParcel(Parcel in) {
return new VideoInfoItem(in);
public VideoPreviewInfo createFromParcel(Parcel in) {
return new VideoPreviewInfo(in);
}
@Override
public VideoInfoItem[] newArray(int size) {
return new VideoInfoItem[size];
public VideoPreviewInfo[] newArray(int size) {
return new VideoPreviewInfo[size];
}
};
}

View File

@@ -0,0 +1,16 @@
package org.schabi.newpipe.services;
import android.graphics.Bitmap;
/**Common properties between VideoInfo and VideoPreviewInfo.*/
public abstract class AbstractVideoInfo {
public String id = "";
public String title = "";
public String uploader = "";
//public int duration = -1;
public String thumbnail_url = "";
public Bitmap thumbnail = null;
public String webpage_url = "";
public String upload_date = "";
public long view_count = -1;
}

View File

@@ -1,4 +1,6 @@
package org.schabi.newpipe;
package org.schabi.newpipe.services;
import org.schabi.newpipe.VideoPreviewInfo;
import java.util.ArrayList;
import java.util.Vector;
@@ -23,13 +25,14 @@ import java.util.Vector;
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
@SuppressWarnings("ALL")
public interface SearchEngine {
class Result {
public String errorMessage = "";
public String suggestion = "";
public Vector<VideoInfoItem> resultList = new Vector<>();
public final Vector<VideoPreviewInfo> resultList = new Vector<>();
}
ArrayList<String> suggestionList(String query);

View File

@@ -1,8 +1,8 @@
package org.schabi.newpipe;
package org.schabi.newpipe.services;
import android.util.Log;
import org.schabi.newpipe.youtube.YoutubeService;
import org.schabi.newpipe.services.youtube.YoutubeService;
/**
* Created by Christian Schabesberger on 23.08.15.
@@ -24,6 +24,10 @@ import org.schabi.newpipe.youtube.YoutubeService;
* 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 ServiceList {
private static final String TAG = ServiceList.class.toString();
private static final StreamingService[] services = {
@@ -40,7 +44,7 @@ public class ServiceList {
}
public static int getIdOfService(String serviceName) {
for(int i = 0; i < services.length; i++) {
if(services[i].getServiceInfo().name == serviceName) {
if(services[i].getServiceInfo().name.equals(serviceName)) {
return i;
}
}

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe;
package org.schabi.newpipe.services;
/**
* Created by Christian Schabesberger on 23.08.15.
@@ -25,7 +25,7 @@ public interface StreamingService {
public String name = "";
}
ServiceInfo getServiceInfo();
Extractor getExtractorInstance();
VideoExtractor getExtractorInstance(String url);
SearchEngine getSearchEngineInstance();
/**When a VIEW_ACTION is caught this function will test if the url delivered within the calling

View File

@@ -0,0 +1,118 @@
package org.schabi.newpipe.services;
/**
* Created by Christian Schabesberger on 10.08.15.
*
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
* VideoExtractor.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/>.
*/
import org.schabi.newpipe.VideoInfo;
/**Scrapes information from a video streaming service (eg, YouTube).*/
@SuppressWarnings("ALL")
public abstract class VideoExtractor {
protected final String pageUrl;
protected VideoInfo videoInfo;
@SuppressWarnings("WeakerAccess")
public VideoExtractor(String url) {
this.pageUrl = url;
}
/**Fills out the video info fields which are common to all services.
* Probably needs to be overridden by subclasses*/
public VideoInfo getVideoInfo()
{
if(videoInfo == null) {
videoInfo = new VideoInfo();
}
if(videoInfo.webpage_url.isEmpty()) {
videoInfo.webpage_url = pageUrl;
}
if(videoInfo.title.isEmpty()) {
videoInfo.title = getTitle();
}
if(videoInfo.duration < 1) {
videoInfo.duration = getLength();
}
if(videoInfo.uploader.isEmpty()) {
videoInfo.uploader = getUploader();
}
if(videoInfo.description.isEmpty()) {
videoInfo.description = getDescription();
}
if(videoInfo.view_count == -1) {
videoInfo.view_count = getViews();
}
if(videoInfo.upload_date.isEmpty()) {
videoInfo.upload_date = getUploadDate();
}
if(videoInfo.thumbnail_url.isEmpty()) {
videoInfo.thumbnail_url = getThumbnailUrl();
}
if(videoInfo.id.isEmpty()) {
videoInfo.id = getVideoId(pageUrl);
}
/** Load and extract audio*/
if(videoInfo.audioStreams == null) {
videoInfo.audioStreams = getAudioStreams();
}
/** Extract video stream url*/
if(videoInfo.videoStreams == null) {
videoInfo.videoStreams = getVideoStreams();
}
if(videoInfo.uploader_thumbnail_url.isEmpty()) {
videoInfo.uploader_thumbnail_url = getUploaderThumbnailUrl();
}
if(videoInfo.startPosition < 0) {
videoInfo.startPosition = getTimeStamp();
}
//Bitmap thumbnail = null;
//Bitmap uploader_thumbnail = null;
//int videoAvailableStatus = VIDEO_AVAILABLE;
return videoInfo;
}
protected abstract String getVideoUrl(String videoId);
protected abstract String getVideoId(String siteUrl);
protected abstract int getTimeStamp();
protected abstract String getTitle();
protected abstract String getDescription();
protected abstract String getUploader();
protected abstract int getLength();
protected abstract int getViews();
protected abstract String getUploadDate();
protected abstract String getThumbnailUrl();
protected abstract String getUploaderThumbnailUrl();
protected abstract VideoInfo.AudioStream[] getAudioStreams();
protected abstract VideoInfo.VideoStream[] getVideoStreams();
}

View File

@@ -1,4 +1,4 @@
package org.schabi.newpipe.youtube;
package org.schabi.newpipe.services.youtube;
import android.net.Uri;
import android.util.Log;
@@ -7,8 +7,8 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.schabi.newpipe.Downloader;
import org.schabi.newpipe.SearchEngine;
import org.schabi.newpipe.VideoInfoItem;
import org.schabi.newpipe.services.SearchEngine;
import org.schabi.newpipe.VideoPreviewInfo;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
@@ -62,7 +62,7 @@ public class YoutubeSearchEngine implements SearchEngine {
String site;
String url = builder.build().toString();
//if we've been passed a valid language code, append it to the URL
if(languageCode.length() > 0) {
if(!languageCode.isEmpty()) {
//assert Pattern.matches("[a-z]{2}(-([A-Z]{2}|[0-9]{1,3}))?", languageCode);
site = Downloader.download(url, languageCode);
}
@@ -95,26 +95,26 @@ public class YoutubeSearchEngine implements SearchEngine {
// both types of spell correction item
if(!((el = item.select("div[class*=\"spell-correction\"]").first()) == null)) {
result.suggestion = el.select("a").first().text();
// search message item
// search message item
} else if(!((el = item.select("div[class*=\"search-message\"]").first()) == null)) {
result.errorMessage = el.text();
// video item type
// video item type
} else if(!((el = item.select("div[class*=\"yt-lockup-video\"").first()) == null)) {
VideoInfoItem resultItem = new VideoInfoItem();
VideoPreviewInfo resultItem = new VideoPreviewInfo();
Element dl = el.select("h3").first().select("a").first();
resultItem.webpage_url = dl.attr("abs:href");
try {
Pattern p = Pattern.compile("v=([0-9a-zA-Z-]*)");
Matcher m = p.matcher(resultItem.webpage_url);
m.find();
resultItem.id=m.group(1);
} catch (Exception e) {
e.printStackTrace();
}
resultItem.title = dl.text();
resultItem.duration = item.select("span[class=\"video-time\"]").first()
.text();
resultItem.duration = item.select("span[class=\"video-time\"]").first().text();
resultItem.uploader = item.select("div[class=\"yt-lockup-byline\"]").first()
.select("a").first()
.text();
@@ -132,7 +132,8 @@ public class YoutubeSearchEngine implements SearchEngine {
}
result.resultList.add(resultItem);
} else {
Log.e(TAG, "GREAT FUCKING ERROR");
//noinspection ConstantConditions
Log.e(TAG, "unexpected element found:\""+el+"\"");
}
}
return result;

View File

@@ -1,8 +1,8 @@
package org.schabi.newpipe.youtube;
package org.schabi.newpipe.services.youtube;
import org.schabi.newpipe.StreamingService;
import org.schabi.newpipe.Extractor;
import org.schabi.newpipe.SearchEngine;
import org.schabi.newpipe.services.StreamingService;
import org.schabi.newpipe.services.VideoExtractor;
import org.schabi.newpipe.services.SearchEngine;
/**
@@ -33,8 +33,13 @@ public class YoutubeService implements StreamingService {
return serviceInfo;
}
@Override
public Extractor getExtractorInstance() {
return new YoutubeExtractor();
public VideoExtractor getExtractorInstance(String url) {
if(acceptUrl(url)) {
return new YoutubeVideoExtractor(url);
}
else {
throw new IllegalArgumentException("supplied String is not a valid Youtube URL");
}
}
@Override
public SearchEngine getSearchEngineInstance() {

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

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