Initial commit
This commit is contained in:
commit
26959020b7
15 changed files with 307 additions and 0 deletions
7
.editorconfig
Normal file
7
.editorconfig
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[*.cr]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/doc/
|
||||||
|
/lib/
|
||||||
|
/bin/
|
||||||
|
/.shards/
|
||||||
|
/video_info/
|
||||||
|
/.vscode/
|
||||||
|
visor
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2017 Omar Roth
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
27
README.md
Normal file
27
README.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# visor
|
||||||
|
|
||||||
|
TODO: Write a description here
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
TODO: Write installation instructions here
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
TODO: Write usage instructions here
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
TODO: Write development instructions here
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
1. Fork it ( https://github.com/[your-github-name]/visor/fork )
|
||||||
|
2. Create your feature branch (git checkout -b my-new-feature)
|
||||||
|
3. Commit your changes (git commit -am 'Add some feature')
|
||||||
|
4. Push to the branch (git push origin my-new-feature)
|
||||||
|
5. Create a new Pull Request
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
- [[your-github-name]](https://github.com/[your-github-name]) Omar Roth - creator, maintainer
|
7
assets/css/grids-responsive-min.css
vendored
Normal file
7
assets/css/grids-responsive-min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
11
assets/css/pure-min.css
vendored
Normal file
11
assets/css/pure-min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
4
assets/js/jquery-min.js
vendored
Normal file
4
assets/js/jquery-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
17
shard.yml
Normal file
17
shard.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
name: visor
|
||||||
|
version: 0.1.0
|
||||||
|
|
||||||
|
authors:
|
||||||
|
- Omar Roth <omarroth@hotmail.com>
|
||||||
|
|
||||||
|
targets:
|
||||||
|
visor:
|
||||||
|
main: src/visor.cr
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
kemal:
|
||||||
|
github: kemalcr/kemal
|
||||||
|
|
||||||
|
crystal: 0.23.1
|
||||||
|
|
||||||
|
license: MIT
|
0
src/proxy.cr
Normal file
0
src/proxy.cr
Normal file
69
src/visor.cr
Normal file
69
src/visor.cr
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
require "kemal"
|
||||||
|
require "xml"
|
||||||
|
require "http/client"
|
||||||
|
require "base64"
|
||||||
|
|
||||||
|
macro templated(filename)
|
||||||
|
render "views/#{{{filename}}}.ecr", "views/layout.ecr"
|
||||||
|
end
|
||||||
|
|
||||||
|
context = OpenSSL::SSL::Context::Client.insecure
|
||||||
|
client = HTTP::Client.new("www.youtube.com", 443, context)
|
||||||
|
|
||||||
|
def params_to_hash(params)
|
||||||
|
pairs = params.split("&")
|
||||||
|
hash = Hash(String, String).new
|
||||||
|
pairs.each do |pair|
|
||||||
|
key, value = pair.split("=")
|
||||||
|
hash[key] = URI.unescape(value)
|
||||||
|
end
|
||||||
|
return hash
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/" do |env|
|
||||||
|
templated "index"
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/watch/:video_id" do |env|
|
||||||
|
video_id = env.params.url["video_id"]
|
||||||
|
|
||||||
|
if File.exists?("video_info/#{video_id}")
|
||||||
|
video_info = JSON.parse(File.open("video_info/#{video_id}"))
|
||||||
|
else
|
||||||
|
video_info_encoded = HTTP::Client.get("https://www.youtube.com/get_video_info?video_id=#{video_id}&el=info&ps=default&eurl=&gl=US&hl=en", nil, nil, tls = context).body
|
||||||
|
video_info = params_to_hash(video_info_encoded)
|
||||||
|
|
||||||
|
File.write("video_info/#{video_id}", video_info.to_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
fmt_stream_map = video_info["url_encoded_fmt_stream_map"].to_s.split(",")
|
||||||
|
fmt_stream = Array(Hash(String, String)).new
|
||||||
|
fmt_stream_map.each do |fmt|
|
||||||
|
fmt_stream << params_to_hash(fmt.to_s)
|
||||||
|
end
|
||||||
|
fmt_stream.reverse!
|
||||||
|
templated "watch"
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/listen/:video_id" do |env|
|
||||||
|
video_id = env.params.url["video_id"]
|
||||||
|
|
||||||
|
if File.exists?("video_info/#{video_id}")
|
||||||
|
video_info = JSON.parse(File.open("video_info/#{video_id}"))
|
||||||
|
else
|
||||||
|
video_info_encoded = HTTP::Client.get("https://www.youtube.com/get_video_info?video_id=#{video_id}&el=info&ps=default&eurl=&gl=US&hl=en", nil, nil, tls = context).body
|
||||||
|
video_info = params_to_hash(video_info_encoded)
|
||||||
|
|
||||||
|
File.write("video_info/#{video_id}", video_info.to_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
adaptive_fmt = Array(Hash(String, String)).new
|
||||||
|
video_info["adaptive_fmts"].to_s.split(",") do |fmt|
|
||||||
|
adaptive_fmt << params_to_hash(video_info["adaptive_fmts"].to_s)
|
||||||
|
end
|
||||||
|
templated "listen"
|
||||||
|
end
|
||||||
|
|
||||||
|
public_folder "assets"
|
||||||
|
|
||||||
|
Kemal.run
|
0
views/index.ecr
Normal file
0
views/index.ecr
Normal file
23
views/layout.ecr
Normal file
23
views/layout.ecr
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Visor</title>
|
||||||
|
<meta content="">
|
||||||
|
<link rel="stylesheet" href="/css/pure-min.css">
|
||||||
|
<link rel="stylesheet" href="/css/grids-responsive-min.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="pure-g">
|
||||||
|
<div class="pure-u-1 pure-u-md-1-5"></div>
|
||||||
|
<div class="pure-u-1 pure-u-md-3-5">
|
||||||
|
<%= content %>
|
||||||
|
</div>
|
||||||
|
<div class="pure-u-1 pure-u-md-1-5"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
16
views/listen.ecr
Normal file
16
views/listen.ecr
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<h1><%= URI.unescape(video_info["title"].to_s,true) %></h1>
|
||||||
|
<video style="width: 100%" poster="<%= video_info["iurlmq"] %>" controls>
|
||||||
|
<% adaptive_fmt.each do |fmt| %>
|
||||||
|
<% fmt_type = fmt["type"].to_s.split(";")[0] %>
|
||||||
|
<% if fmt_type.starts_with?("audio") %>
|
||||||
|
<source src="<%= fmt["url"] %>" type="<%= fmt_type %>">
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</video>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="pure-g">
|
||||||
|
<div class="pure-u-1 pure-u-md-1-5"></div>
|
||||||
|
<div class="pure-u-1 pure-u-md-3-5"></div>
|
||||||
|
<div class="pure-u-1 pure-u-md-1-5"></div>
|
||||||
|
</div>
|
15
views/watch.ecr
Normal file
15
views/watch.ecr
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<h1><%= URI.unescape(video_info["title"].to_s,true) %></h1>
|
||||||
|
<video style="width: 100%" poster="<%= video_info["iurlmq"] %>" controls>
|
||||||
|
<% fmt_stream.each do |fmt| %>
|
||||||
|
<source src="<%= fmt["url"] %>" type="<%= fmt["type"].to_s.split(";")[0] %>">
|
||||||
|
<% end %>
|
||||||
|
</video>
|
||||||
|
<div class="pure-g">
|
||||||
|
<div class="pure-u-1 pure-u-md-1-5"></div>
|
||||||
|
<div class="pure-u-1 pure-u-md-3-5">
|
||||||
|
<% fmt_stream.each do |fmt| %>
|
||||||
|
<p><%= fmt["quality"] %></p>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="pure-u-1 pure-u-md-1-5"></div>
|
||||||
|
</div>
|
83
youtube api.md
Normal file
83
youtube api.md
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
# YOUTUBE API URLS
|
||||||
|
|
||||||
|
## Search
|
||||||
|
```curl
|
||||||
|
https://www.youtube.com/results BASE URL
|
||||||
|
?q= QUERY
|
||||||
|
&page= PAGE
|
||||||
|
|
||||||
|
&sp=EgIQAVAU FOR STREAM
|
||||||
|
&sp=EgIQAlAU FOR CHANNEL
|
||||||
|
&sp=EgIQA1AU FOR PLAYLIST
|
||||||
|
```
|
||||||
|
|
||||||
|
## Channel
|
||||||
|
```curl
|
||||||
|
https://www.youtube.com/feeds/videos.xml BASE URL
|
||||||
|
?channel_id= CHANNEL ID
|
||||||
|
|
||||||
|
https://www.youtube.com/channel/:ID/videos CLEAN URL
|
||||||
|
|
||||||
|
?view=0 VIDEO PARAMS
|
||||||
|
&flow=list |
|
||||||
|
&sort=dd |
|
||||||
|
&live_view=10000 |
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stream
|
||||||
|
```curl
|
||||||
|
https://www.youtube.com/get_video_info BSAE URL
|
||||||
|
?video_id= VIDEO_ID
|
||||||
|
&el=info
|
||||||
|
&ps=default
|
||||||
|
&eurl=
|
||||||
|
&gl=US
|
||||||
|
&hl=en
|
||||||
|
|
||||||
|
https://www.youtube.com/embed/:ID
|
||||||
|
|
||||||
|
|
||||||
|
https://www.youtube.com/get_video_info?video_id= + video_id +
|
||||||
|
&el=info&ps=default&eurl=&gl=US&hl=en"&el=info&ps=default&eurl=&gl=US&hl=en
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Pulled from [here](https://github.com/TeamNewPipe/NewPipeExtractor/tree/master/src/main/java/org/schabi/newpipe/extractor/services/youtube)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
ctoken EhgSBmNvbG9ycxoOU0l3QlVCVHFBd0ElM0QYvN7oGA%253D%253D
|
||||||
|
itct CBoQuy8iEwjrn57J94vWAhXFn64KHUCuBss%3D
|
||||||
|
|
||||||
|
ctoken EhoSBmNvbG9ycxoQU0ZCUUZPb0RBQSUzRCUzRBi83ugY
|
||||||
|
itct CBoQuy8iEwj4g4TF94vWAhUBZq4KHdF7DWs%3D
|
||||||
|
|
||||||
|
ctoken EhoSBmNvbG9ycxoQU0dSUUZPb0RBQSUzRCUzRBi83ugY
|
||||||
|
itct CBoQuy8iEwierb_G94vWAhUTdq4KHcWVCTI%3D
|
||||||
|
|
||||||
|
ctoken EhoSBmNvbG9ycxoQU0hoUUZPb0RBQSUzRCUzRBi83ugY
|
||||||
|
itct CBoQuy8iEwiZwfvH94vWAhXCiq4KHa82A70%3D
|
||||||
|
|
||||||
|
ctoken EhoSBmNvbG9ycxoQU0JSUUZPb0RBQSUzRCUzRBi83ugY
|
||||||
|
itct CD0Quy8YACITCOKdxPf2i9YCFVBOrgodqy8K0Cj0JA%3D%3D
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Hex 12 1a 12 06
|
||||||
|
Hex 12 18 12 06
|
||||||
|
Padding?
|
||||||
|
| Query: colors
|
||||||
|
| |
|
||||||
|
ctoken EhgS BmNvbG9ycx oOU0 l3 QlVCVHFBd0ElM0QYvN7oGA%253D%253D
|
||||||
|
ctoken EhoS BmNvbG9ycx oQU0 ZC UUZPb0RBQSUzRCUzRBi83ugY
|
||||||
|
ctoken EhoS BmNvbG9ycx oQU0 dS UUZPb0RBQSUzRCUzRBi83ugY
|
||||||
|
ctoken EhoS BmNvbG9ycx oQU0 ho UUZPb0RBQSUzRCUzRBi83ugY
|
||||||
|
ctoken EhoS BmNvbG9ycx oQU0 JS UUZPb0RBQSUzRCUzRBi83ugY
|
||||||
|
|
||||||
|
itct CBoQuy8iEw jrn57J 94vWAh XFn 64KH UCuBss%3D
|
||||||
|
itct CBoQuy8iEw j4g4TF 94vWAh UBZ q4KH dF7DWs%3D
|
||||||
|
itct CBoQuy8iEw ierb_G 94vWAh UTd q4KH cWVCTI%3D
|
||||||
|
itct CBoQuy8iEw iZwfvH 94vWAh XCi q4KH a82A70%3D
|
||||||
|
itct CD0Quy8YAC ITCOKd xPf2i9 YCF VBOr godqy8K0Cj0JA%3D%3D
|
||||||
|
```
|
Loading…
Reference in a new issue