Giter Site home page Giter Site logo

clipface's Introduction

Clippy Mc. Clipface

Super simple, self hosted clip sharing application.

Features

  • Simple, intuitive user interface
  • No config required, hosts your existing clip folder
  • Super simple deployement with official Docker image
  • Simple but informative clip list with real time search
  • Optional authentication to protect your clips
  • "Copy public link" for copying clip links that circumvents user authentication for a single clip
  • Uses OpenGraph metadata so videos are automatically embedded in Facebook posts, Discord messages etc.

Watch page

Clip list

Usage with Docker

First of all pull the Docker image:

$ docker pull tomsan/clipface

Very simple usage, no authentication, port 80:

docker run -d \
  --name clipface \
  -v /host/path/to/clips:/clips \
  -p 80:80 \
  tomsan/clipface:latest

For more advanced usage, you need to provide Clipface with some configuration. (See Configuration.) Here is an example where we require authentication and set the clip list page title to "Bob's clips":

docker run -d \
  --name clipface \
  -v /host/path/to/clips:/clips \
  -p 80:80 \
  -e CLIPFACE_USER_PASSWORD="password123" \
  -e CLIPFACE_CLIPS_PAGE_TITLE="Bob's clips" \
  tomsan/clipface:latest

Configuration

Clipface uses node-config for configuration management. This means that Clipface can be configured using a config file or by setting environment variables (or both.) For Docker deployments, using environment variables is the most convenient option.

If you would rather use a config file than environment variables, you can use config/default.toml as a reference. Mount the resulting file to /config/local.toml inside the container. Any setting you put in your config file will override the corresponding setting from the default config file. If you want to leave a parameter at its default value, simply omit it from your config file.

List of config parameters:

  • clips_path - The absolute path of the directory containing the clips that Clipface should host. This defaults to "/clips", which is a convenient value for Docker images.

    Default value: "/clips"
    Environment variable: CLIPFACE_CLIPS_PATH

  • pagination - If true, the clip list will be split into pages. This is highly recommended if your clip count is in the hundreds, as it will hugely improve the responsiveness of the site. The end user can choose the amount of clips displayed per page.

    Default value: true
    Environment variable: CLIPFACE_PAGINATION

  • user_password - A password used to protect this Clipface instance. If set, users must input this password before they can watch any clips or see the list of clips. By default this parameter is not set, which will allow anybody to browse and watch all your clips.

    Default value: (unset)
    Environment variable: CLIPFACE_USER_PASSWORD

  • secure_cookies - If set to true (which is the default value), the "secure" setting will be used for the authication cookie, which means the cookie will only be included when using SSL (HTTPS). If you are not using SSL, you need to set this option to false, or authentication won't work.

    Default value: true
    Environment variable: CLIPFACE_SECURE_COOKIES

  • header_title - Title displayed in the header on all pages

    Default value: "Clippy Mc. Clipface"
    Environment variable: CLIPFACE_HEADER_TITLE

  • clips_page_title - Title displayed on the clip list page

    If not set (which is the default), no title will be displayed and the header will be significantly smaller.

    Default value: (unset)
    Environment variable: CLIPFACE_CLIPS_PAGE_TITLE

NGINX reverse proxy with SSL

For the best security, you should run Clipface behind a SSL-enabled reverse proxy, especially if you are using authentication. Otherwise, passwords will be transferred in plain text over the internet, which is always a bad idea.

Here is an example NGINX configuration that uses certificates from Let's Encrypt:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name my-clipface-domain.com;

    ssl_certificate /etc/letsencrypt/live/my-clipface-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/my-clipface-domain.com/privkey.pem;

    # SSL config below generated by: ssl-config.mozilla.org

    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;

    ssl_dhparam dhparam.pem;

    # Intermediate configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # HSTS (ngx_http_headers_module is required) (63072000 seconds)
    add_header Strict-Transport-Security "max-age=63072000" always;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # Replace with the IP address of your resolver
    resolver 1.1.1.1 1.1.2.2;

    location / {
        proxy_pass http://clipface/;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 3s;
        proxy_send_timeout 10s;
        proxy_read_timeout 300s;
        client_max_body_size 100m;
    }
}

You must replace the URL in proxy_pass http://clipface/ with your local Clipface address, for example http://127.0.0.1:3000. In my case I'm running both Clipface and NGINX as Docker containers in the same Docker network, so I'm simply referring to Clipface by its container name, "clipface".

NB: The "X-Forwarded-*" headers are required for the server to know its own URL, which is needed for certain server-side rendered meta tags. If you don't configure these headers, things like embedding Discord videos will fail.

NB: This config assumes you have the file "dhparam.pem" in your NGINX root config directory. If you don't, you can generate it like this: openssl dhparam -out /my/nginx/config/path/dhparam.pem 2048.

NB: The Strict-Transport-Security header informs browsers to always use HTTPS towards your domain. This will break any HTTP (not HTTPS) applications you are hosting on your domain, so enable with care.

Authentication

Clipface supports simple password authentication by setting the user_password option in the config file. This will redirect users to a login screen where the configured password must be entered before the user can proceed.

For security reasons, the hashed password is stored in a HTTP-only cookie. Clipface assumes (again for security reasons) that you will be running it behind a reverse proxy with SSL enabled, so the "secure_cookies" config option defaults to true. This means that the authentication cookie is created with the "secure" flag, which means it will only be transferred when the HTTPS protocol is used. If you are running Clipface without SSL (not using HTTPS), you should set the "secure_cookies" option to false in the config file, otherwise authentication will not work. Be aware that passwords will be transferred in plain text in this case.

If you see any issues or have any concerns about security, please open an issue on Github.

Single clip tokens

Clipface will automatically generate single clip authentication tokens when the "Copy public link" button is pressed. These tokens only allow access to a single clip.

They are generated by hashing the clip name with the configured user password (plus some salt). This means that all single clip auth tokens can be invalidated by changing the user password. Single clip auth tokens for a specific clip can be invalidated by renaming the clip.

Roadmap

See the Milestones on GitHub for planned features.

Troubleshooting

If you have an issue with Clipface, please skim the list of known issues below. If they don't apply to you, please ask a question in the Discussions page or open a new issue.

Embeds in Discord are slow / not loading

This is probably not an issue with Clipface. Discord seems to have an issue with larger clips, since all clips are proxied through Discord's servers. Clips above 100MB will fail entirely. Ref: #20 (comment)

If your clips are of a reasonable bitrate and size, please confirm that the server you are hosting Clipface on has sufficient bandwidth.

clipface's People

Contributors

f-dav avatar gregoryjjb avatar hubro 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

clipface's Issues

Login box CSS is broken on first page load

When logging out, the login box looks ok. If you refresh the page, it breaks. Only the styles applied by styled-components appear to break, and only when server side rendered (i.e. navigating directly to the page.)

After pressing "Log out":

image

After reloading the page:

image

Table sorting

Make it possible to sort by each of the table columns. The "Saved" column should be the default.

Suggestion: Autoplay?

Is there any way to have the site autoplay the video once it's loaded? It autoplays when clicking on the video from the list but not when going directly to the video page from a public link.

Suggestion: Unlisted video mode

I'd like to know if it's possible to have an "unlisted" mode - Videos are viewable by everyone but the list of videos is only locked to admin. That way you can share links to people without having a really long URL (no token needed) and without having to worry about them seeing other vides on the site.

Add admin authentication

Add optional admin authentication using the admin_password config key.

If provided, this password will unlock administrative actions in Clipface, like uploading/renaming/deleting clips and adding/editing clip titles and descriptions.

Admin authentication will use the same login box as the regular user authentication.

If user authentication is enabled, then enabling admin authentication will not make any visible difference to the user interface. If the admin password is entered in the password box, the user will be logged in as admin. If already logged in as a regular user, the user must log back out and then log in as the admin user.

If user authentication is not enabled but admin authentication is enabled, a new button will be added in the header called "Log in" to allow the user to log in as admin.

SSL/Reverse Proxy Troubles

I am a total noob at configuring nginx and reverse proxies and even using Docker. (only other time I used it was to install smokeping on a Raspberry Pi).

I was wondering if there was a way to install an SSL certificate using openssl instead of going through a reverse proxy? I am having so much trouble with nginx. I got the proxy pass to work, but it cannot access any of the video files for whatever reason.

Honestly if anyone could share how they configured their reverse proxy that'd be great. Really want my links to embed into Discord, but this is not possible without https.

Generate thumbnails for videos and expose them via opengraph media tags

Hey Hubro, a buddy of mine and I forked Clipface to mess around with it a bit to have some fun, but one of the things we did was test max's theory about the og:image. Turns out max is correct. I tested a 150MB clip and it didn't load, then we just forced every video to use the same test image and restarted clipface with that change. Then, the embed instantly loaded, but with the placeholder image there and now the 150MB file plays in Discord embedded.

Here is the commit on our fork where we made that change

Interesting! So I guess the reason Discord downloads the video is in order to generate a thumbnail image.

I guess I have to add thumbnail generation ๐Ÿ˜„

Originally posted by @Hubro in #20 (comment)

Embeds Taking Long Time To Load Now (Discord)

I am not sure if this is related to Clipface, but recently something has gone a little weird with the embedding. Now, the longer a clip is, the longer it takes for Discord to load the embed player. Sometimes, if the clip is too long, Discord won't even load it and will show the poop error image. I was using Cloudflare to get a free SSL certificate and I even tried bypassing their cache, but that isn't solving the issue. I was wondering if this is related to me having more and more clips in the clipface directory slowing it down or is this just a Discord thing? I almost feel like Discord's servers are trying to load the entire video before loading the embed player.

Example: https://imgur.com/COeY16O

EDIT: This honestly just might be random. A 34 second clip also generates the poop error image on Discord and won't bring up an embed player.

Dark Mode

Even just a pitch black AMOLED style mode would be great. Theatre mode is good, but Clipface still has bright white elements. I have used browser extensions in the past, but quit using them because sometimes they just start bugging out. Honestly, it doesn't even need to be a toggle on the webpage. Just something you can type into the docker run arguments when starting Clipface.

Public URL vs API link

Hi, nice project here. Got a question for you on how to use this

So, I'm using the configuration with user password, so the page is hidden behind a password. I'm using this to embed videos on another site.

By default, the URL of the video is like http://domain.com/watch/filename.mp4

And in order to share it publicly, you can use the "copy public link" to share it with ?token

However, if I right click on the video and select "Open video in new tab", I get this URL without auth.
http://domain.com/api/video/filename.mp4

What does this mean to you?

Add clip metadata

If a JSON file with the same name as a clip exists, load clip title and description from that file. The title and description should be displayed with the clip in a layout similar to YouTube.

New Embed Feature Not Working

I pulled latest from Docker and I cannot get the embed to work. At first I thought it was because my local DNS may have been messing it up, so I swapped my DNS and it still won't load. I tried a brand new clip as well and it didn't work, but as I am typing this I just noticed something. Above the clip name it shows the IP it is trying to connect to and that IP is 127.0.0.1:2095 which is the IP and port my NGINX reverse proxy is pointing towards.

https://imgur.com/a/jHO4day

Scale well on mobile devices

Clipface was not made with mobile scalability in mind and currently it starts breaking when the window width goes below 800px or so. This is not good enough for 2021.

Shorter URLs

Someone posted a Streamable link today and it reminded me of how short their URLs are. Is it possible to generate a 6-8 character long URL that redirects to the full URL without using third-party websites/services? Since you'd be redirecting to the long URLs, if you change your password not only would the original links fail to load anything now, so would the shorter URLs because they just redirect to the full URL.

I did find an open source service called Kutt and they have an API you can generate keys for. Now, that isn't really self hosted because you're relying on them to redirect the URL for you, but that might be an easier way of doing it? I just thought there could be a way to enable/disable short URLs when you load the docker image and then if you enable it, every time you copy a URL, it would generate a 6-8 digit URL and then whenever Clipface sees someone using that short URL, it redirects them to the real one in a browser.

It just looks a lot cleaner in a chat. I'll post a comparison below.

https://imgur.com/a/RsMrihs

EDIT: That imgur link just made me think of something, you could make it easy for Clipface to recognize a short URL by replacing the /watch/ part of a URL with something like /u/ or another letter. Like how imgur uses /a/. Right? I don't know. I really need to learn how to code. ๐Ÿ˜…

EDIT2: Actually, it wouldn't even have to be a starting argument on Docker. It could just be a button next to the other URL buttons: https://imgur.com/MCmzbr7

EDIT3: Now that I think about it, the URLs can't really be randomly generated. They'd have to be generated similarly to how you generate the public URLs.

Authentication not working

After reviewing #37 it seems I have a similar but not solvable in the same way situation. when attempting to log in, the website hangs, and then after refreshing and trying again, it redirects to a blank webpage with the text
{"__N_SSP":true,"pageProps":{"__N_REDIRECT":"/login?next=%2F_next%2Fdata%2FKQ6EOlWR4ZVZDVRR4cJF3%2Findex.json","__N_REDIRECT_STATUS":307}} and does not continue.

local.toml:

user_password = "12345"
secure_cookies = false

docker logs clipface output:

$ next start -p 80
ready - started server on 0.0.0.0:80, url: http://localhost:80
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }
User logged in successfully
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }
User logged in successfully
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }
User logged in successfully
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }
User logged in successfully
Local settings set to { theaterMode: false, videoVolume: 1, clipsPerPage: 40 }

Please let me know if I can help by supplying any more information

Cant login.

Whenever I try to log in on clipface,I put in the password and it either infinitely loads, or I get this error message.

Screenshot 2022-10-03 225501

But whenever I try to log in without any passwords, it works fine.

Authentication is not working

I'm using the Docker image to install your application. I'm also running the clipface application behind a reverse proxy with SSL enabled and using letsencrypt certification. I've tried setting secure_cookies to false but that did not help either.

Problem:
When setting a password through the environment variable CLIPFACE_USER_PASSWORD, the page becomes password-protected but I am not able to log in with the set password. I've tried many passwords (including password123 from the example). It just tells me the password is wrong.

Latest Docker Container Fails to Start

I was running this before, but when I pulled the latest version I no longer can get the docker container to start. Below is what is being logged to the console.

$ next start -p 39456

ready - started server on 0.0.0.0:39456, url: http://localhost:39456

Error: Configuration property "header_title" is not defined

at Config.get (/app/node_modules/config/lib/config.js:182:11)

at Object.<anonymous> (/app/next.config.js:6:29)

at Module._compile (node:internal/modules/cjs/loader:1101:14)

at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)

at Module.load (node:internal/modules/cjs/loader:981:32)

at Function.Module._load (node:internal/modules/cjs/loader:822:12)

at Module.require (node:internal/modules/cjs/loader:1005:19)

at require (node:internal/modules/cjs/helpers:94:18)

at Object.loadConfig [as default] (/app/node_modules/next/dist/server/config.js:345:34)

at async NextServer.loadConfig (/app/node_modules/next/dist/server/next.js:112:22)

error Command failed with exit code 1.

info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Remember page position when going back to clip list

Expected behavior:

  • Browse to page X
  • Watch a clip
  • Press "Back to clips" or just "Back" in the browser history
  • Be at the same page and scroll position, ready to keep browsing

Current behavior:

When you click "Back to clips" or the browser's "Back" button, you're taken back to page 1 and scrolled back to the top of the page.

Copy link / copy public link buttons don't work

Whenever I press either of the buttons I get a "failed to copy link" javascript error.

The /create-single-clip-token network call succeeds, and returns a json object with a token key/value. But I see a js error in the console:

image

(DRAFT) Transcoding

THIS DRAFT IS A WORK IN PROGRESS

Comments are welcome though


Add the possibility of doing automatic transcoding of clips to other qualities.

I am a big believer in the Unix philosophy of "do one thing and do it well". Since "streaming clips over the internet" and "doing automatic video transcoding" is two very different things, I would like to avoid putting them into the same application.

For that reason, I propose writing a new application that will automatically transcode all clips in a directory into multiple qualities. This application can be deployed onto the same clip folder as Clipface. Clipface can be modified to find the alternative video sources and make them available in the user interface.

Suggested application name: clipface-transcode. (Creative, I know.)

Overview

The simplest way to enable transcoding will be to deploy the official clipface-transcoding Docker image on the same clip directory that Clipface is using. The transcode process will place finished lower-quality clips into a subdirectory called .clipface-transcode/. Clipface will detect these clips and display them as quality options in the user interface.

Changes to Clipface

On the front end, the user will now see options for other qualities. By default, "Source" will be selected. When the user selects a different quality, the lower quality clip will be played instead. The choice will be saved as a local user setting and that quality will be used for all clips.

Transcoding process

TODO

Challenges

  • What do we do when clips are deleted? How do we reliably delete the orphaned transcoded clips?
    • A cleanup function that runs daily (?) could loop through the transcoded clips and confirm that their source still exists, and if not, delete them.
  • If the transcoded clips are keyed by clip name, how will we handle the original clips being renamed? Transcoding the clip all over again just because it has a new name seems very wasteful.
    • Key transcoded clips by MD5 hash instead? This will survive clip renames, but will be very computationally expensive for large clips. The hashes will have to be cached and associated with the clip name.

Add drag-and-drop uploading of clips

Add simple drag-and-drop clip uploading. This should only be enabled when the user is logged in as admin.

When a clip is dragged from a file manager into the clip list in the browser, it should automatically be uploaded and added to the clips folder.

There should also be a upload button at the top of the clip list in case drag-and-drop is not an option.

Add support for ShareX?

Can you add support for ShareX to upload clips that are recorded using it? Any plans to add image upload support?

Possible to configure nginx subfolder?

Is it possible to configure clipface on a subfolder? It works great if it place in the root location block in my nginx config, but if I try to do it on a subfolder I get 404 errors.

something like /clips instead of the root?:

location /clips {
proxy_pass http://clipface/;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 3s;
proxy_send_timeout 10s;
proxy_read_timeout 300s;
client_max_body_size 100m;
}

"An unexpected error has occurred." on Ubuntu 20.04

I have a feeling this is related to the path to the folder containing the clips, but when I start the container in docker and go to the web server, it is blank and there is a small text that says "An unexpected error has occurred." I have tried several ports and tried different ways of typing the file path and have had no luck. Currently I am replacing "-v /host/path/to/clips:/clips " with "-v /mnt/PLEXTHREE/Clips:/clips ".

Is that what I am doing wrong? Other than that the only thing I could think of is I am missing some dependencies.

"failed to compute cache key" when building image on Windows

Hopefully this is the right place to ask this because I have searched around trying to figure out what is wrong, but cannot seem to do it. I am trying to build the docker image on Windows so I can start working on it so I can begin learning how to code and also possibly help develop Clipface. When building the docker image using docker build --pull --rm -f "Dockerfile" -t clipface:latest "." I get the following:

[+] Building 1.7s (6/8)
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 300B                                                                               0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/node:alpine                                                     1.6s
 => [internal] load build context                                                                                  0.0s
 => => transferring context: 28B                                                                                   0.0s
 => CANCELED [1/4] FROM docker.io/library/node:alpine@sha256:e64dc950217610c86f29aef803b123e1b6a4a372d6fa4bcf71f9  0.0s
 => => resolve docker.io/library/node:alpine@sha256:e64dc950217610c86f29aef803b123e1b6a4a372d6fa4bcf71f9ddcbd39eb  0.0s
 => => sha256:e64dc950217610c86f29aef803b123e1b6a4a372d6fa4bcf71f9ddcbd39eba5c 1.43kB / 1.43kB                     0.0s
 => => sha256:3888dace40316274a4666539d239e081976a01c2e460f3230b1bf49433d805f8 1.16kB / 1.16kB                     0.0s
 => => sha256:bb1fcdaff9369c498f6161d85f8a082c10012c0a8d6b9c76f5140abce7841c78 6.53kB / 6.53kB                     0.0s
 => ERROR [2/4] ADD client/docker-bundle.tgz /                                                                     0.0s
------
 > [2/4] ADD client/docker-bundle.tgz /:
------
failed to compute cache key: "/client/docker-bundle.tgz" not found: not found

What am I doing wrong?

Add pagination of clip list

The rendering performance of the clip list is suffering as the number of clips increases. Add pagination of the clip list to keep navigation snappy. It's important that if a user is navigating clips on page other than the first page, watches a clip and then presses "back", they should be taken to the same page they were on, and preferably also the same scroll position.

The number of clips per page should be configurable from the front-end, but the default number should also be configurable from the config file with the key clips_per_page.

Blank Playblack

Hi all
I couldn't find an issue with mine but I have the docker container setup and everything looks good however when playing back videos the video is blank but the audio works fine

Add WebM support to take advantage of the VP9 codec

Just found out I can encode my clips in VP9 very easy since I updated avidemux and after comparing VP9 and H264 renders it is amazing how at 50% bitrate the quality is identical and Discord supports .webm files so I was wondering if this could be added in a future update of clipface or maybe I am missing something. Thanks :)

Upload videos

Is there any plan to support video uploads? This would be great so my friends can upload and share videos on Discord.

Thanks

(linux/arm64/v8)

Whenever I run the docker script I get this error: "WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested".

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.