Giter Site home page Giter Site logo

teamnewpipe / newpipeextractor Goto Github PK

View Code? Open in Web Editor NEW
1.1K 63.0 369.0 106.86 MB

NewPipe's core library for extracting data from streaming sites

License: GNU General Public License v3.0

Java 99.82% JavaScript 0.18%
newpipe youtube crawler extractor soundcloud peertube mediaccc bandcamp scraper

newpipeextractor's Introduction

NewPipe Extractor

CI JIT Pack Badge JDocDocumentation

NewPipe Extractor is a library for extracting things from streaming sites. It is a core component of NewPipe, but could be used independently.

Usage

NewPipe Extractor is available at JitPack's Maven repo.

If you're using Gradle, you could add NewPipe Extractor as a dependency with the following steps:

  1. Add maven { url 'https://jitpack.io' } to the repositories in your build.gradle.
  2. Add implementation 'com.github.TeamNewPipe:NewPipeExtractor:INSERT_VERSION_HERE' to the dependencies in your build.gradle. Replace INSERT_VERSION_HERE with the latest release.
  3. If you are using tools to minimize your project, make sure to keep the files below, by e.g. adding the following lines to your proguard file:
## Rules for NewPipeExtractor
-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; }
-keep class org.mozilla.javascript.** { *; }
-keep class org.mozilla.classfile.ClassFileWriter
-dontwarn org.mozilla.javascript.tools.**

Note: To use NewPipe Extractor in Android projects with a minSdk below 26, API desugaring is required. If the minSdk is below 19, the desugar_jdk_libs_nio artifact is required, which requires Android Gradle Plugin (AGP) version 7.4.0.

Testing changes

To test changes quickly you can build the library locally. A good approach would be to add something like the following to your settings.gradle:

includeBuild('../NewPipeExtractor') {
    dependencySubstitution {
        substitute module('com.github.TeamNewPipe:NewPipeExtractor') with project(':extractor')
    }
}

Another approach would be to use the local Maven repository, here's a gist of how to use it:

  1. Add mavenLocal() in your project repositories list (usually as the first entry to give priority above the others).
  2. It's recommended that you change the version of this library (e.g. LOCAL_SNAPSHOT).
  3. Run gradle's ìnstall task to deploy this library to your local repository (using the wrapper, present in the root of this project: ./gradlew install)
  4. Change the dependency version used in your project to match the one you chose in step 2 (implementation 'com.github.TeamNewPipe:NewPipeExtractor:LOCAL_SNAPSHOT')

Tip for Android Studio users: After you make changes and run the install task, use the menu option File → "Sync with File System" to refresh the library in your project.

Supported sites

The following sites are currently supported:

  • YouTube
  • SoundCloud
  • media.ccc.de
  • PeerTube (no P2P)
  • Bandcamp

License

GNU GPLv3 Image

NewPipe Extractor 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 as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

newpipeextractor's People

Contributors

antoninkriz avatar audricv avatar b0pol avatar chowder avatar coffeemakr avatar connectety avatar dependabot[bot] avatar firemasterk avatar fynngodau avatar gzsombor avatar isira-seneviratne avatar kapodamy avatar karyogamy avatar koitharu avatar litetex avatar mauriciocolli avatar opusforlife2 avatar petlyh avatar redirion avatar royosef avatar stypox avatar tacothedank avatar thescrabi avatar theta-dev avatar tobigr avatar triallax avatar vkay94 avatar wb9688 avatar xiangronglin avatar yausername avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

newpipeextractor's Issues

NewPipeExtractor is Broken

The Extractor has started to fail for everything from viewing channels to playing videos itself.

Videos give no error but just fail to load and say "Content unavailble" and viewing "What's New" or any channel in general results in an error stating "Could not parse website".

I am using the latest NewPipe (0.14.1) on Android 8.1.0-27.

Edit: Editing for adding the trace of the error message when you try to open a channel ---

Exception

Crash log

org.schabi.newpipe.extractor.exceptions.ParsingException: Could not get channel name
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor.getName(YoutubeChannelExtractor.java:99)
	at org.schabi.newpipe.extractor.channel.ChannelInfo.getInfo(ChannelInfo.java:59)
	at org.schabi.newpipe.extractor.channel.ChannelInfo.getInfo(ChannelInfo.java:48)
	at org.schabi.newpipe.util.ExtractorHelper.lambda$getChannelInfo$4$ExtractorHelper(ExtractorHelper.java:121)
	at org.schabi.newpipe.util.ExtractorHelper$$Lambda$4.call(Unknown Source:4)
	at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:44)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.maybe.MaybeFromSingle.subscribeActual(MaybeFromSingle.java:41)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.drain(MaybeConcatArray.java:153)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.request(MaybeConcatArray.java:78)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe$ElementAtSubscriber.onSubscribe(FlowableElementAtMaybe.java:66)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray.subscribeActual(MaybeConcatArray.java:42)
	at io.reactivex.Flowable.subscribe(Flowable.java:14349)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe.subscribeActual(FlowableElementAtMaybe.java:36)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeToSingle.subscribeActual(MaybeToSingle.java:46)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
	at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:579)
	at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
	at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
	at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.jsoup.nodes.Element.attr(java.lang.String)' on a null object reference
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor.getName(YoutubeChannelExtractor.java:97)
	... 28 more


Streams with signature don't work anymore

After the refactoring videos with signatures don't play anymore We have to fix that asap. Since this is an important part of the extractor.
I took a look a the code, and it seems that the error exists somewhere here.
This is the line where the new URL with the decrypted signature gets created.

However the extractor of the current working version of NewPipe v0.9.10 does seem to use the same code. So it's a bit more tricky. Hover I'm not at home right now so I can't use my computer. Otherwise I would have checked the URLs the old extractor produces, and compared it to the URLs the new one produces.

Other services

Is this code already ready for adding other services? If so, could I start implementing some other services?

[YouTube] Wrong view count gets extracted

This issue exists already in NewPipe (TeamNewPipe/NewPipe#1714) but since it's more an issue in the extractor for the Youtube service I created it here too.

On some videos youtube places "Recommended for you" instead of e.g. "188K views" so the newpipe extractor semms not to know what to do with that and returns 0 views.

YouTube search filters

Hi! I wanted to provide some information on how YouTube's search filters work, as I noticed currently NewPipe uses hardcoded values. Sorry if this is the wrong place for this.

The &sp= parameter is a Base64'd protocol buffer that contains all the filters. I'd recommend looking here for a working implementation. It's pretty short, but essentially you have (about) two bytes for each filter, one (or more) bytes as a type, and then the other byte as a value, for example:

date month
0x08 0x04

Features (live, HD, ...) have a value of 1:

HD   true
0x20 0x01

HDR       true
0xc8 0x01 0x01

Once you've added all your filters, give it a header (which contains the only mandatory filter, sort, see the code above for details), Base64 encode, URI escape, and you're good to go.

Hopefully this is useful, and I want to thank the NewPipe team for providing their excellent work for others.

Remove static variables from extractors

As I said here (comment), we need to remove these static variables, because (among another reasons) let's say that I want to extract multiple channels at the same time, with the current static variables one "instance" will interfere with the other, not pretty...

Here's a gist demonstrating that.

PS: Another problem is that you can't request a specific page from a channel, but let's fix that after this is fixed.

Fails with OkHttp

I tried to use NewPipeExtractor with OkHttp, but it failed extracting videos with a DecryptException. So I decided to add some System.out.printlns and I found out OkHttp returns a different YouTube page as HttpsURLConnectiom. Here you could find my Downloader.java. I'll look into why NewPipeExtractor fails with it and I'll try to fix it. I'll also create a PR to NewPipe for using OkHttp if you want.

Add YouTube video comments scrapper

As I understand it, the comments are not loaded directly you have to scroll down to see/fetch them, or you could do it by using this prefix: https://www.youtube.com/all_comments?v={youtube_id}
That's how it's done in Python: youtube-comment-downloader

It's something that a lot of users are asking for in the NewPipe Android app, if someone is willing to find a way to scrape the comment section of a video that'd be awesome, and greatly appreciated!

Rework README.md

Now since we offer NewPIpeExtractor as a maven repo we might consider to rework the README.md. Also we might add a guide for how to develop the extractor/own services.

crash with most videos

Exception

org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor$DecryptException: Could not parse decrypt function 
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor.loadDecryptionCode(YoutubeStreamExtractor.java:722)
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor.onFetchPage(YoutubeStreamExtractor.java:597)
	at org.schabi.newpipe.extractor.Extractor.fetchPage(Extractor.java:48)
	at org.schabi.newpipe.extractor.stream.StreamInfo.getInfo(StreamInfo.java:59)
	at org.schabi.newpipe.extractor.stream.StreamInfo.getInfo(StreamInfo.java:55)
	at org.schabi.newpipe.util.ExtractorHelper.lambda$getStreamInfo$3$ExtractorHelper(ExtractorHelper.java:113)
	at org.schabi.newpipe.util.ExtractorHelper$$Lambda$3.call(Unknown Source:4)
	at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:44)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.maybe.MaybeFromSingle.subscribeActual(MaybeFromSingle.java:41)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.drain(MaybeConcatArray.java:153)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.request(MaybeConcatArray.java:78)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe$ElementAtSubscriber.onSubscribe(FlowableElementAtMaybe.java:66)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray.subscribeActual(MaybeConcatArray.java:42)
	at io.reactivex.Flowable.subscribe(Flowable.java:14349)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe.subscribeActual(FlowableElementAtMaybe.java:36)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeToSingle.subscribeActual(MaybeToSingle.java:46)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
	at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:579)
	at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
	at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
	at java.lang.Thread.run(Thread.java:764)
Caused by: org.schabi.newpipe.extractor.utils.Parser$RegexException: failed to find pattern "(["\'])signature\1\s*,\s*([a-zA-Z0-9$]+)\(
	at org.schabi.newpipe.extractor.utils.Parser.matchGroup(Parser.java:63)
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor.loadDecryptionCode(YoutubeStreamExtractor.java:702)
	... 30 more

RegexException when trying to load YouTube decryption code

A RegexException "failed to find pattern "(var SK={.+?}};)" occurs in YouTubeVideoStreamExtractor.loadDecryptionCode(String) in this line:

helperObject = Parser.matchGroup1(helperPattern, playerCode);

I tried for example with this url: https://www.youtube.com/watch?v=fKFbnhcNnjE.

Affected NewPipe version is 0.11.5.

I'd like to post the value of playerCode variable here but this exceeds the allowed message length.
But I can send it to you in another way if you like to.

The watch count of LIVE streams is treated as the upload date.

While working on #61, I've come across this bug of the YouTube service:
The watch count of LIVE streams is treated as the upload date. This doesn't matter if the date is just displayed, but the relative time parser fails.
This happens on the channel page and on the feed and in related videos under another stream. The bug is most likely in the YoutubeStreamInfoItemExtractor.

org.schabi.newpipe...ParsingException: Unable to parse the date: 103,985 watching
  at org.schabi.newpipe...TimeAgoParser.parseTimeUnit(TimeAgoParser.java:80)
  ...

liveyt livenp
Everything look right in the pictures, but the watch count is actually omitted (by if (infoItem.view_count >= 0)) and the 103,985 watching is the upload date.

prevent Extractor form fetching

I noticed we still fetch data in the constructor of the extractors. We use the fetchPage() function for fetching, but I think it would be better if this function would be called manually and not through the extractor.

Shared YouTube URLs don't work anymore

Shared YouTube URLs don't work anymore, since they seem to require authentication now. So we'll need to either find a workaround (if there even is one) or drop the code for shared YouTube URLs.

Stateful UrlIdHandler

A recent change to UrlIdHandler causes it to now keep track of the last id and original url, which is then used for resolving a usable url for the extractor. However, since this handler is a singleton, when multiple asynchronous calls the url handler to change url state, a race condition occurs where most of these calls will use an existing dirty url state for further extraction. Currently, one of the ways this problem manifests in the client is when multiple different streams url are being resolved at the same time, the extractor will send back the same StreamInfo.

@theScrabi Can you please take a look into this? This needs to be fixed or rolled back before the next version of NewPipe is released as it potentially breaks all asynchronous calls to the extractor from the client.

error couldn't get next video

this error only seens to happen in age restricted videos

Exception

org.schabi.newpipe.extractor.exceptions.ParsingException: Could not get next video
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor.getNextVideo(YoutubeStreamExtractor.java:503)
	at org.schabi.newpipe.extractor.stream.StreamInfo.extractOptionalData(StreamInfo.java:239)
	at org.schabi.newpipe.extractor.stream.StreamInfo.getInfo(StreamInfo.java:64)
	at org.schabi.newpipe.extractor.stream.StreamInfo.getInfo(StreamInfo.java:55)
	at org.schabi.newpipe.util.ExtractorHelper.lambda$getStreamInfo$3$ExtractorHelper(ExtractorHelper.java:113)
	at org.schabi.newpipe.util.ExtractorHelper$$Lambda$3.call(Unknown Source:4)
	at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:44)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.maybe.MaybeFromSingle.subscribeActual(MaybeFromSingle.java:41)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.drain(MaybeConcatArray.java:153)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.request(MaybeConcatArray.java:78)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe$ElementAtSubscriber.onSubscribe(FlowableElementAtMaybe.java:66)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray.subscribeActual(MaybeConcatArray.java:42)
	at io.reactivex.Flowable.subscribe(Flowable.java:14349)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe.subscribeActual(FlowableElementAtMaybe.java:36)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeToSingle.subscribeActual(MaybeToSingle.java:46)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
	at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:579)
	at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
	at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
	at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'org.jsoup.select.Elements org.jsoup.nodes.Element.select(java.lang.String)' on a null object reference
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor.getNextVideo(YoutubeStreamExtractor.java:499)
	... 29 more

-------------------

Can't pull HD

C4 Pedro - Tu És a Mulher

Want to download it. But let's be honest anything less that 720p video/audio combo sucks. How can this be Improved so the app can survive?

[YouTube] NullPointerException on getErrorMessage() calls for some streams

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.jsoup.nodes.Element.text()' on a null object reference
at org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor.getErrorMessage(YoutubeStreamExtractor.java:517)

public String getErrorMessage() {
    String errorMessage = doc.select("h1[id=\"unavailable-message\"]").first().text();

videoId: -97j9dP7SKU

Create instances of StreamingServices on the front end.

Would it be possible to create instances of the StreamingServices on the front end and pass them to the extractor via function call?
This would have several advantages:

  • It would eliminate the need for the awkward ServiceList enum. Is the following a list of services or a single service?

     ServiceList serviceItem;
  • It would give the front end a proper way to obtain instances of the StreamingServices.
    The constructors should take all parameters necessary to create a particular service.
    This would allow the front end to store settings of the services. For example YouTube might take information about the language in order to parse relative dates.

  • It would allow for services to be implemented outside of this project which would be extremely useful for those who want to use the NewPipeExtractor as a library.

How this idea could be implemented:

public class NewPipe {
    private static final Map<Integer, StreamingService> registeredServices = new HashMap<>();

    public static void registerService(StreamingService service) {
        // TODO
    }
}

Change Regex and Selector Strings to Data/Language (Live) Parsing

The Problem

In this project the most data grabing things are done with a quick and dirty way (like always on the web), by using regex and selector strings.

Theoretic

The problem with this is regex and selector strings are not made for grabing data from the source. Selector strings come from css and jQuery and are mostly designed for create static websites (css) and interact with the xml of the html (jQuery/bouncer). Regex goes more in the direction of data grabing. But both got no high level functions implemented (join, trim, split) and on a lowlevel base the implementation is hard and mostly cost resources. Also Regex is not made for Language Parsing which made it hard for use it in the web.

Practical

This is used for parsing the client_id of soundcloud app from the javascript code.
,client_id:"(.*?)"
This regex is problematic because it depends on a name, even depends on a name writing norm and on a language norm of defining variables.

The name is less problematic than the other factors, because currently the most web projects are not completly minified, but still is a problem, because the complete minifying is possible and can change the name of key, id and endpoint variables.

The writing norm is somehow the same game like the name, but even on top of these this is not completly normed on the web. Because the Developers come from different directions some from python that write in the pip norm, some from Java with the Cemal Case Norm, even pure Javascript/EMACscript Developer or just other Developer that do not realy follow a Norm.

The language norm is the most problematic one Javascript/EMACscript is one of the most complex languages that exists, this caused by the number of version that get published without breaking with old norms. In this case the problem is the set/init(:) and split/separator(,) operator. Both got different characters that can be used, because their can be viewed in different contexts. The version of this regex strongly depends on shorthand object definition ({key:value,key:{...}, ...}) and even do not using trimmed strings, that easly can be passed by one simple space. Data can also be asigned by the using these = (, ; .) chars and a endpoint operator let var this.

[var |let |this.|x.]key=value,key=value,...

Solution

Language Norm Problem

Parsing languages and data formats. Live from the stream or from the completely downloaded data. Using a set of the following solutions for the single case. 😄

Best solution should be a whole new project or a web service that providing the id, keys, endpoints and other data are needed for using the private and public apis of the services.

Name Problem

Here you can use regex or programming rules on the extracted value, to get the right formated value and try it with try and error. Maybe you need to filter the values because of the attack with simply spam right values into the source code.

Writing Norm Problem

Parse different norms over and over again. Of Course this can be attack easily but with word parsing it can be nearly impossible. Maybe create a service for the ones who using the app for submit new words through the word database.

[question] Is Newpipe illegal?

According to the terms of service of Google ( https://www.google.com/intl/en/policies/terms/regional.html ) :
"Don’t misuse our Services. For example, don’t interfere with our Services or try to access them using a method other than the interface and the instructions that we provide. "

  • Parsing a website is probably not a provided instruction.

Sorr y if this has been already asked, I could not find such an issue.

Compatibility with Video Hostings

  • I carefully read the contribution guidelines and agree to them.
  • I checked if the issue/feature exists in the latest version.

I would like Newpipe to be compatible with Video Hostings such as streamplay, streamcloud, streamango, powvideo, gamovideo, etc... and the search engine in these cases would be used to enter the URL that directs you to the video.

Could not get channel name

Exception

org.schabi.newpipe.extractor.exceptions.ParsingException: Could not get channel name
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor.getName(YoutubeChannelExtractor.java:99)
	at org.schabi.newpipe.extractor.channel.ChannelInfo.getInfo(ChannelInfo.java:59)
	at org.schabi.newpipe.extractor.channel.ChannelInfo.getInfo(ChannelInfo.java:48)
	at org.schabi.newpipe.util.ExtractorHelper.lambda$getChannelInfo$4$ExtractorHelper(ExtractorHelper.java:121)
	at org.schabi.newpipe.util.ExtractorHelper$$Lambda$4.call(Unknown Source:4)
	at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:44)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.maybe.MaybeFromSingle.subscribeActual(MaybeFromSingle.java:41)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.drain(MaybeConcatArray.java:153)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.request(MaybeConcatArray.java:78)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe$ElementAtSubscriber.onSubscribe(FlowableElementAtMaybe.java:66)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray.subscribeActual(MaybeConcatArray.java:42)
	at io.reactivex.Flowable.subscribe(Flowable.java:14349)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe.subscribeActual(FlowableElementAtMaybe.java:36)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeToSingle.subscribeActual(MaybeToSingle.java:46)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.maybe.MaybeFromSingle.subscribeActual(MaybeFromSingle.java:41)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeSubscribeOn$SubscribeTask.run(MaybeSubscribeOn.java:54)
	at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:38)
	at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:26)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
	at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.jsoup.nodes.Element.attr(java.lang.String)' on a null object reference
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor.getName(YoutubeChannelExtractor.java:97)
	... 28 more

-------------------

Decide classes naming between channel/user

This pull request modified the "Channel" naming to "User".
I've been thinking a bit more about this change, and I wanted to know the opinion of you all about it.

EDIT: I meant which naming, specifically for the classes of the extractor, do you prefer.

Just to know, this would impact just the way someone uses the extractor, for example:

UserInfo.getInfo("url"); 
// or
ChannelInfo.getInfo("url");

Thinking again, I don't even know why I created an issue for such small detail, but since it's already created I'll leave open anyway to know your opinion about it.

Testing and decouple extractor

As @TheAssassin said here, I want to see the tests working too.

I'm thinking in doing this:

  • Move the unit tests to here
  • Make this repository "buildable" with gradle
  • Use jitpack.io to import it to NewPipe as a dependency (I think that @theScrabi said that he wanted to include in maven right? For now, jitpack will be enough)

Currently, doesn't make much sense having the tests there, a big reason is:

  • For each commit that is done there, the unit tests start ALL again testing this repository that remains intact, we are just repeating the tests without changing anything

Another reason would be that is easier to modify the extractor or include in another project, as it'll be a totally independent module, for now, it is a little bit coupled with NewPipe repository...

What do you guys think? If you have suggestions, feel free to let me know.

add HDR Support

Although tunneled playback is enabled on the player in the client, without proper extractor support, HDR will not work.

Here's a snippet from youtube-dl, as you can see, all HDR content has Itag of 33x:

youtube-dl.exe -F https://www.youtube.com/watch?v=WW2DKBGCvEs
[youtube] WW2DKBGCvEs: Downloading webpage
[youtube] WW2DKBGCvEs: Downloading video info webpage
[youtube] WW2DKBGCvEs: Extracting video information
[youtube] WW2DKBGCvEs: Downloading MPD manifest
[info] Available formats for WW2DKBGCvEs:
format code extension resolution note
139 m4a audio only DASH audio 48k , m4a_dash container, mp4a.40.5@ 48k (22050Hz), 683.89KiB
249 webm audio only DASH audio 55k , opus @ 50k, 771.41KiB
250 webm audio only DASH audio 80k , opus @ 70k, 1.07MiB
140 m4a audio only DASH audio 127k , m4a_dash container, mp4a.40.2@128k (44100Hz), 1.78MiB
171 webm audio only DASH audio 132k , vorbis@128k, 1.73MiB
251 webm audio only DASH audio 151k , opus @160k, 2.03MiB
160 mp4 256x144 DASH video 47k , avc1.4d400c, 24fps, video only, 470.93KiB
278 webm 256x144 144p 97k , webm container, vp9, 24fps, video only, 1.24MiB
330 webm 256x144 144p HDR 147k , vp9.2, 24fps, video only, 1.73MiB
242 webm 426x240 240p 173k , vp9, 24fps, video only, 1.82MiB
133 mp4 426x240 DASH video 182k , avc1.4d4015, 24fps, video only, 1.83MiB
331 webm 426x240 240p HDR 241k , vp9.2, 24fps, video only, 2.96MiB
134 mp4 640x360 DASH video 280k , avc1.4d401e, 24fps, video only, 2.81MiB
243 webm 640x360 360p 364k , vp9, 24fps, video only, 3.96MiB
332 webm 640x360 360p HDR 457k , vp9.2, 24fps, video only, 5.80MiB
135 mp4 854x480 DASH video 631k , avc1.4d401e, 24fps, video only, 6.27MiB
244 webm 854x480 480p 662k , vp9, 24fps, video only, 7.10MiB
333 webm 854x480 480p HDR 864k , vp9.2, 24fps, video only, 10.99MiB
136 mp4 1280x720 DASH video 1319k , avc1.4d401f, 24fps, video only, 13.16MiB
247 webm 1280x720 720p 1466k , vp9, 24fps, video only, 16.15MiB
334 webm 1280x720 720p HDR 1821k , vp9.2, 24fps, video only, 22.38MiB
248 webm 1920x1080 1080p 2723k , vp9, 24fps, video only, 32.02MiB
137 mp4 1920x1080 DASH video 2875k , avc1.640028, 24fps, video only, 28.16MiB
335 webm 1920x1080 1080p HDR 3181k , vp9.2, 24fps, video only, 40.02MiB
271 webm 2560x1440 1440p 7477k , vp9, 24fps, video only, 86.30MiB
264 mp4 2560x1440 DASH video 7973k , avc1.640032, 24fps, video only, 79.24MiB
336 webm 2560x1440 1440p HDR 11586k , vp9.2, 24fps, video only, 143.43MiB
337 webm 3840x2160 2160p HDR 19690k , vp9.2, 24fps, video only, 260.42MiB
266 mp4 3840x2160 DASH video 21183k , avc1.640033, 24fps, video only, 218.04MiB
313 webm 3840x2160 2160p 21927k , vp9, 24fps, video only, 245.45MiB
17 3gp 176x144 small , mp4v.20.3, mp4a.40.2@ 24k
36 3gp 320x180 small , mp4v.20.3, mp4a.40.2
43 webm 640x360 medium , vp8.0, vorbis@128k
18 mp4 640x360 medium , avc1.42001E, mp4a.40.2@ 96k
22 mp4 1280x720 hd720 , avc1.64001F, mp4a.40.2@192k (best)

Add all Itags

Currently the NewPipeExtractor doesn't handle all Itags. Some because "it's not well supported in older devices" (e.g. Opus). Some because we "Don't add VideoOnly streams that have normal variants". Some because we don't have added HDR support. Some others are just missing. IMHO we should just add all Itags, since NewPipeExtractor is just an extractor. We should let the frontend (e.g. NewPipe most of the time) handle choosing the stream from the available ones. Also the FPS is often incorrect (30 instead of 24).

[Refactor][Nice to have] rename timeStamp to streamTimeStamp or move to StreamUrlIdHandler

Its quit confusing if you want to implement a service getTimeStamp is in the context of the StreamExtractor and not the upload date or something like this. It would be better to move this to the StreamUrlIdHandler because its a url parsing part and not something that is in the meta data of the stream, like the most data that is can be accessed over the StreamExtractor

++ extractor/src/main/java/org/schabi/newpipe/extractor/stream/StreamUrlIdHandler.java

public interface StreamUrlIdHandler extends UrlIdHandler {
String getTimeStamp(String url) throws ParsingException;
}

or directly in the living standard implementation of url parsing that comes with DTube and MediaCCC support. (Url Parsing get maybe moved to a whole new project)

Relevent for new implementations.
And currently in the DTube Implementation (already fixed local):
Wrong Implementation
|_ Usage of Wrong Implementation
|_ Base Needs to be removed
|_ Base Needs to be removed now

Need help adding SoundCloud support again

@wb9688 I am currently deep refactoring the search part of the NewPipe extractor. The Search engine was some sort of prototype of the later coming ListExtractors. They are more advanced, and fit better into the architecture which is why I wanted to get rid of the search engine. However I am currently trying to focus on supporting Youtube, with the time I have I don't know if I can also add support for SoundCloud. In order to not have to take care about SoundCloud right now I am going to remove. If the development is done we can add it again, though I would like to aks you for help.

[YouTube] Disabled features on community guidelines critical videos

Youtube decides to deactivate Features on community guideline critical Videos.
Currently I only see it on this video "https://www.youtube.com/watch?v=6iMuMKjaU4w", but I also see videos of rappers that get in similar mode "https://www.youtube.com/watch?v=p-BfaQQxuO4".

The problem we got with the first video is that we could not get the channel url (which is deactivated by Youtube). Also we could not get the next Video. And we could not get the uploader thumbnail url.

I would be better to get a log system and only throw an error if we detecting that the video is not an community guidline critical video.

add mixcloud support

SoundCloud is nice and thank you for the support of it.
But it's a bit of mainstream music and boring. Try Mixcloud, you'll find a million streams of professional music maker.
Regarda

What's new doesn't work and videos show content not avaliable but no error message

This is the error that happens when opening the app with what's new

Exception

Crash log

org.schabi.newpipe.extractor.exceptions.ParsingException: Could not get channel name
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor.getName(YoutubeChannelExtractor.java:99)
	at org.schabi.newpipe.extractor.channel.ChannelInfo.getInfo(ChannelInfo.java:59)
	at org.schabi.newpipe.extractor.channel.ChannelInfo.getInfo(ChannelInfo.java:48)
	at org.schabi.newpipe.util.ExtractorHelper.lambda$getChannelInfo$4$ExtractorHelper(ExtractorHelper.java:121)
	at org.schabi.newpipe.util.ExtractorHelper$$Lambda$4.call(Unknown Source:4)
	at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:44)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.maybe.MaybeFromSingle.subscribeActual(MaybeFromSingle.java:41)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.drain(MaybeConcatArray.java:153)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.request(MaybeConcatArray.java:78)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe$ElementAtSubscriber.onSubscribe(FlowableElementAtMaybe.java:66)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray.subscribeActual(MaybeConcatArray.java:42)
	at io.reactivex.Flowable.subscribe(Flowable.java:14349)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe.subscribeActual(FlowableElementAtMaybe.java:36)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeToSingle.subscribeActual(MaybeToSingle.java:46)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.maybe.MaybeFromSingle.subscribeActual(MaybeFromSingle.java:41)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeSubscribeOn$SubscribeTask.run(MaybeSubscribeOn.java:54)
	at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:38)
	at io.reactivex.internal.schedulers.ScheduledDirectTask.call(ScheduledDirectTask.java:26)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
	at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String org.jsoup.nodes.Element.attr(java.lang.String)' on a null object reference
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor.getName(YoutubeChannelExtractor.java:97)
	... 28 more


D.tube support

D.tube is a decentralized video streaming platform, backed by the IPFS Network and the STEEM Blockchain.

IPFS is used for torrent-style hosting while STEEM is used for decentralized account management, comments, ratings and more.
Everything there is open source but didn't get the recognition which it deserves

Add llucy support.

llucy is an alluc successor, a service that searches for videos in various streaming sites.

I can try implementing this myself (though I haven't done any real android development before so it may take a while or just straight-up fail) but was wondering, given the legal-but-shady nature of the site, if that's a service that'd be welcome as an addition to NewPipe.

(Bug) Could not get duration (also suggestion)

Exception

(This happens with EVERY video btw)

The problem is that I live in Finland and we have some inconsistencies in following international standards, especially in time/decimal notation, and here the time separator is . (dot) instead of the more common : (punctuation colon).

The problem is that when this program expects a : in the timeformat thing, but youtube returns instead a dot (based on geographic location), which causes errors and in some cases for the app to hang partially.
For example 4:48 is 4.48 here (it also looks awful, like a decimal number, national standards cannot be done anything to though :) gotta just live with it)

I do not have the technical knowhow to compile this program again (well I managed to compile this extractor part to .jar but don't know how to shove it into the main program / compile it), BUT I know something about programming and I determined that the problem is fixed by changing this on line 33 in the file

/NewPipeExtractor/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/linkHandler/YoutubeParsingHelper.java

from

String[] splitInput = input.split(":");

to

String[] splitInput = input.split("\\."); 

Could the fix be to add something before that split thing that the split character equals : if content language variable equals something? Like (don't know exactly but)

if contentLang = "US" then splitCharacter = ":" 
if contentLang = "FI" then splitCharacter = "\\."

(of course it would be required to look at the wikipedia page for what countries actually have dot and which do not.. and to write that line in proper java syntax lol I can help with the former one if needed.

Sorry if there's something wrong with this. Thank you. This program is incredible btw, I use it everyday

Here's the long error:

org.schabi.newpipe.extractor.exceptions.ParsingException: Could not get Duration: https://www.youtube.com/watch?v=S7de4JJVV8A
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamInfoItemExtractor.getDuration(YoutubeStreamInfoItemExtractor.java:93)
	at org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector.extract(StreamInfoItemsCollector.java:54)
	at org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector.commit(StreamInfoItemsCollector.java:89)
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor.getRelatedVideos(YoutubeStreamExtractor.java:517)
	at org.schabi.newpipe.extractor.utils.ExtractorHelper.getRelatedVideosOrLogError(ExtractorHelper.java:32)
	at org.schabi.newpipe.extractor.stream.StreamInfo.extractOptionalData(StreamInfo.java:249)
	at org.schabi.newpipe.extractor.stream.StreamInfo.getInfo(StreamInfo.java:64)
	at org.schabi.newpipe.extractor.stream.StreamInfo.getInfo(StreamInfo.java:55)
	at org.schabi.newpipe.util.ExtractorHelper.lambda$getStreamInfo$3$ExtractorHelper(ExtractorHelper.java:113)
	at org.schabi.newpipe.util.ExtractorHelper$$Lambda$3.call(Unknown Source)
	at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:44)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.maybe.MaybeFromSingle.subscribeActual(MaybeFromSingle.java:41)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.drain(MaybeConcatArray.java:153)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray$ConcatMaybeObserver.request(MaybeConcatArray.java:78)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe$ElementAtSubscriber.onSubscribe(FlowableElementAtMaybe.java:66)
	at io.reactivex.internal.operators.maybe.MaybeConcatArray.subscribeActual(MaybeConcatArray.java:42)
	at io.reactivex.Flowable.subscribe(Flowable.java:14349)
	at io.reactivex.internal.operators.flowable.FlowableElementAtMaybe.subscribeActual(FlowableElementAtMaybe.java:36)
	at io.reactivex.Maybe.subscribe(Maybe.java:4073)
	at io.reactivex.internal.operators.maybe.MaybeToSingle.subscribeActual(MaybeToSingle.java:46)
	at io.reactivex.Single.subscribe(Single.java:3310)
	at io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
	at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:579)
	at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
	at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
	at java.util.concurrent.FutureTask.run(FutureTask.java:237)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
	at java.lang.Thread.run(Thread.java:841)
Caused by: java.lang.NumberFormatException: Invalid long: "5.42"
	at java.lang.Long.invalidLong(Long.java:124)
	at java.lang.Long.parse(Long.java:361)
	at java.lang.Long.parseLong(Long.java:352)
	at java.lang.Long.parseLong(Long.java:318)
	at org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeParsingHelper.parseDurationString(YoutubeParsingHelper.java:64)
	at org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamInfoItemExtractor.getDuration(YoutubeStreamInfoItemExtractor.java:91)
	... 34 more

-------------------

(the same as above, repeated 10 times for all the suggested videos)

-------------------

Search retrieves translated results

Hello o/
After updating to NewPipe 0.14.1 (from repo) due to the url decryption issue, some of the search results appear in portuguese for me (I'm in Brazil). I've attached two printscreens, one with NewPipe results, one with youtube app results.

newpipe - https://user-images.githubusercontent.com/470809/45434642-49364880-b685-11e8-9bc4-d1caa5d7f170.png
youtube - https://user-images.githubusercontent.com/470809/45434653-4dfafc80-b685-11e8-82ad-146338e8dac2.png

Thanks

Songs

Hi,
Can you add an option to play mp3,m4a and flac songs in new pipe it will be cool.

Add bandcamp support

Is it possible to add support for bandcamp, doesn't require login and have preview and sometimes complete albums available

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.