rget 4.8.4 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.devcontainer/Dockerfile +14 -0
- data/.devcontainer/devcontainer.json +30 -0
- data/.travis.yml +1 -1
- data/README.md +8 -7
- data/bin/hls-dl +18 -0
- data/lib/asobistore.rb +5 -5
- data/lib/freshlive.rb +6 -6
- data/lib/hibiki.rb +3 -1
- data/lib/himalaya.rb +2 -2
- data/lib/hls.rb +19 -0
- data/lib/nicovideo.rb +4 -4
- data/lib/onsen.rb +10 -26
- data/lib/podcast.rb +1 -1
- data/lib/standfm.rb +62 -0
- data/lib/webradio.rb +7 -4
- data/rget.gemspec +3 -3
- data/rget.yaml +9 -38
- metadata +11 -9
- data/bin/ulizadl +0 -103
- data/lib/uliza.rb +0 -37
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 002a0d55ff230dabdb3c046e67cf09a56f28aae9bbbb44c3bd7de3a4490cc770
|
|
4
|
+
data.tar.gz: 33f86195c750fb5cad0f133fe199fc8a94fe65f6a5bae2b0eb372eb7a34aed6f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f144e5b3623c29342f278b22de551b3e535f8ce1a699d6454116718a4653bc4425b49ce3f4e8062cc98ca08e4940e6ce77462456161a99144008e9c5a36a7972
|
|
7
|
+
data.tar.gz: cf9a018da02a9651e554cff2237bebbe744aff02073799b10f02806fd55079e051fc123f2c79802694efc030682126b52eb2f128041d553468a8cdc6564a0e0b
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
FROM ruby:2
|
|
2
|
+
ENV USER vscode
|
|
3
|
+
LABEL maintainer "@tdtds <t@tdtds.jp>"
|
|
4
|
+
RUN apt-get -y update && apt-get -y install ffmpeg && \
|
|
5
|
+
curl -sLo /usr/local/bin/youtube-dl http://www.yt-dl.org/downloads/latest/youtube-dl && \
|
|
6
|
+
chmod +x /usr/local/bin/youtube-dl && \
|
|
7
|
+
useradd -u 1000 -m $USER && chsh -s /bin/bash $USER
|
|
8
|
+
USER $USER
|
|
9
|
+
RUN bundle config set path vendor/bundle && \
|
|
10
|
+
bundle config set with development:test && \
|
|
11
|
+
echo 'git config --global --unset core.editor' >> /home/$USER/.bashrc && \
|
|
12
|
+
echo 'git config --global --unset core.sshCommand' >> /home/$USER/.bashrc && \
|
|
13
|
+
echo 'git ls-remote -q > /dev/null' >> /home/$USER/.bashrc
|
|
14
|
+
CMD [ "sleep", "infinity" ]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
|
|
2
|
+
// https://github.com/microsoft/vscode-dev-containers/tree/v0.122.1/containers/docker-from-docker-compose
|
|
3
|
+
// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml.
|
|
4
|
+
{
|
|
5
|
+
"name": "rget",
|
|
6
|
+
"context": "..",
|
|
7
|
+
"dockerFile": "Dockerfile",
|
|
8
|
+
|
|
9
|
+
// Set *default* container specific settings.json values on container create.
|
|
10
|
+
"settings": {
|
|
11
|
+
"terminal.integrated.shell.linux": "/bin/bash"
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
// Add the IDs of extensions you want installed when the container is created.
|
|
15
|
+
"extensions": [
|
|
16
|
+
"rebornix.ruby"
|
|
17
|
+
],
|
|
18
|
+
|
|
19
|
+
// Uncomment the next line if you want start specific services in your Docker Compose config.
|
|
20
|
+
// "runServices": [],
|
|
21
|
+
|
|
22
|
+
// Uncomment the next line if you want to keep your containers running after VS Code shuts down.
|
|
23
|
+
// "shutdownAction": "none",
|
|
24
|
+
|
|
25
|
+
// Use 'postCreateCommand' to run commands after the container is created.
|
|
26
|
+
// "postCreateCommand": "docker --version",
|
|
27
|
+
|
|
28
|
+
// Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
|
|
29
|
+
// "remoteUser": "vscode"
|
|
30
|
+
}
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
|
@@ -5,9 +5,9 @@ Downloading newest radio programs on the web. Supported radio stations are:
|
|
|
5
5
|
* hibiki
|
|
6
6
|
* onsen
|
|
7
7
|
* niconico
|
|
8
|
-
* freshlive.tv
|
|
9
8
|
* himalaya.fm
|
|
10
|
-
*
|
|
9
|
+
* Asobi Store
|
|
10
|
+
* stand.fm
|
|
11
11
|
|
|
12
12
|
If you want to save files as MP3, needs `ffmpeg` command.
|
|
13
13
|
To download niconico video, also needs latest `youtube-dl` command (you can get it from `https://yt-dl.org/`), then specify niconico user ID and password to `~/.netrc` as:
|
|
@@ -25,9 +25,10 @@ For customize radio programs, copy `rget.yaml` to `~/.rget` or current work dire
|
|
|
25
25
|
* `~/.rget`
|
|
26
26
|
* `<command path>/../rget.yaml` (for gem)
|
|
27
27
|
|
|
28
|
-
## Installation
|
|
28
|
+
## Installation and Setup
|
|
29
29
|
```
|
|
30
30
|
$ gem install rget
|
|
31
|
+
$ rget init > ~/.rget # or your local rget.yaml
|
|
31
32
|
```
|
|
32
33
|
|
|
33
34
|
## Usage
|
|
@@ -41,14 +42,14 @@ rget yaml http://example.com/radio >> ~/.rget
|
|
|
41
42
|
# download and convert to mp3 THE iDOLM@STER Cinderella Girls Radio
|
|
42
43
|
rget imas_cg
|
|
43
44
|
|
|
44
|
-
# download
|
|
45
|
-
rget
|
|
45
|
+
# download suzakinishi only (saving movie file as .MP4)
|
|
46
|
+
rget suzakinishi --no-mp3
|
|
46
47
|
|
|
47
48
|
# srore mp3 file to specified directory
|
|
48
|
-
rget
|
|
49
|
+
rget matsui --path=/home/hoge/radio
|
|
49
50
|
|
|
50
51
|
# store mp3 file to radio folder of Dropbox
|
|
51
|
-
rget
|
|
52
|
+
rget shiny_radio --path=dropbpx://radio
|
|
52
53
|
```
|
|
53
54
|
|
|
54
55
|
### add\_mp3info command
|
data/bin/hls-dl
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require "open3"
|
|
3
|
+
|
|
4
|
+
if ARGV.size != 2
|
|
5
|
+
$stderr.puts "HLS downloader wrapping ffmpeg"
|
|
6
|
+
$stderr.puts "hls-dl <playlist.m3u8> <output(.mp4,mp3,...)>"
|
|
7
|
+
exit 1
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
ffmpeg = %Q[ffmpeg -loglevel error -protocol_whitelist file,http,https,tcp,tls,crypto -n -i "#{ARGV[0]}" #{ARGV[1]}]
|
|
11
|
+
result = Open3.capture3(ffmpeg)
|
|
12
|
+
unless result[2].to_i == 0
|
|
13
|
+
p result
|
|
14
|
+
File.delete(ARGV[1]) if File.exist?(ARGV[1])
|
|
15
|
+
$stderr.puts result[1]
|
|
16
|
+
exit(1)
|
|
17
|
+
end
|
|
18
|
+
exit(0)
|
data/lib/asobistore.rb
CHANGED
|
@@ -10,9 +10,9 @@ class AsobiStore < WebRadio
|
|
|
10
10
|
|
|
11
11
|
def download
|
|
12
12
|
player = find_player(@url)
|
|
13
|
-
html = Nokogiri(open("https:#{player}").read)
|
|
13
|
+
html = Nokogiri(URI.open("https:#{player}").read)
|
|
14
14
|
src_m3u8 = html.css('source').first.attr('src')
|
|
15
|
-
m3u8 = "#{File.dirname(src_m3u8)}/#{open(src_m3u8).read.match(/^[^#].*/)[0]}"
|
|
15
|
+
m3u8 = "#{File.dirname(src_m3u8)}/#{URI.open(src_m3u8).read.match(/^[^#].*/)[0]}"
|
|
16
16
|
|
|
17
17
|
serial = html.title.scan(/#(\d+)/).flatten.first.to_i
|
|
18
18
|
@cover = "https:#{html.css('audio,video').first.attr('poster')}" unless @cover
|
|
@@ -24,7 +24,7 @@ class AsobiStore < WebRadio
|
|
|
24
24
|
agent.get(m3u8)
|
|
25
25
|
body = agent.page.body
|
|
26
26
|
rescue ArgumentError
|
|
27
|
-
body = open(m3u8, &:read)
|
|
27
|
+
body = URI.open(m3u8, &:read)
|
|
28
28
|
end
|
|
29
29
|
tses = body.scan(/.*\.ts.*/)
|
|
30
30
|
key_url = body.scan(/URI="(.*)"/).flatten.first
|
|
@@ -52,10 +52,10 @@ class AsobiStore < WebRadio
|
|
|
52
52
|
|
|
53
53
|
private
|
|
54
54
|
def find_player(url)
|
|
55
|
-
programs = Nokogiri(open(url).read)
|
|
55
|
+
programs = Nokogiri(URI.open(url).read)
|
|
56
56
|
programs.css('.list-main-product a.wrap').each do |program|
|
|
57
57
|
begin
|
|
58
|
-
return Nokogiri(open("https://asobistore.jp#{program.attr('href')}").read).css('iframe').last.attr('src')
|
|
58
|
+
return Nokogiri(URI.open("https://asobistore.jp#{program.attr('href')}").read).css('iframe').last.attr('src')
|
|
59
59
|
rescue # access denied because only access by premium members
|
|
60
60
|
next
|
|
61
61
|
end
|
data/lib/freshlive.rb
CHANGED
|
@@ -10,7 +10,7 @@ class FreshLive < WebRadio
|
|
|
10
10
|
archive = URI(File.join(@url + '/programs/archive'))
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
each_programs(Nokogiri(open(archive).read)) do |meta|
|
|
13
|
+
each_programs(Nokogiri(URI.open(archive).read)) do |meta|
|
|
14
14
|
begin
|
|
15
15
|
serial = meta['data']['title'].scan(/\d+$/).first.to_i
|
|
16
16
|
src = "#{@label}##{'%02d' % serial}.ts"
|
|
@@ -24,7 +24,7 @@ class FreshLive < WebRadio
|
|
|
24
24
|
print "getting #{src}..."
|
|
25
25
|
ts_list(meta['data']['archiveStreamUrl']).each_with_index do |u, i|
|
|
26
26
|
print '.' if i % 50 == 0
|
|
27
|
-
w.write(open(u, 'rb').read)
|
|
27
|
+
w.write(URI.open(u, 'rb').read)
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
end
|
|
@@ -62,7 +62,7 @@ class FreshLive < WebRadio
|
|
|
62
62
|
}
|
|
63
63
|
else
|
|
64
64
|
tag = Pathname(u.path).basename.to_s
|
|
65
|
-
meta = JSON.parse(Nokogiri(open(@url, &:read)).css('script').first)
|
|
65
|
+
meta = JSON.parse(Nokogiri(URI.open(@url, &:read)).css('script').first)
|
|
66
66
|
return {
|
|
67
67
|
tag => {
|
|
68
68
|
'desc' => meta['name'],
|
|
@@ -78,13 +78,13 @@ private
|
|
|
78
78
|
x = "//section[descendant::h1[contains(text(),'アーカイブ')]]//*[contains(@class,'ProgramTitle')]/a/@href"
|
|
79
79
|
html.xpath(x).each do |href|
|
|
80
80
|
id = Pathname(href.value).basename.to_s
|
|
81
|
-
yield JSON.parse(open("https://freshlive.tv/proxy/Programs;id=#{id}", &:read))
|
|
81
|
+
yield JSON.parse(URI.open("https://freshlive.tv/proxy/Programs;id=#{id}", &:read))
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def ts_list(rate_m3u8)
|
|
86
|
-
ts_m3u8 = open(rate_m3u8).read.each_line.grep_v(/^#/)[1].chomp
|
|
87
|
-
open(URI(rate_m3u8) + ts_m3u8).read.each_line.grep_v(/^#/).map{|u|URI(rate_m3u8) + u.chomp}
|
|
86
|
+
ts_m3u8 = URI.open(rate_m3u8).read.each_line.grep_v(/^#/)[1].chomp
|
|
87
|
+
URI.open(URI(rate_m3u8) + ts_m3u8).read.each_line.grep_v(/^#/).map{|u|URI(rate_m3u8) + u.chomp}
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def to_mp3(src)
|
data/lib/hibiki.rb
CHANGED
|
@@ -57,7 +57,9 @@ private
|
|
|
57
57
|
playlist_url = video_info[:playlist_url]
|
|
58
58
|
m3u8_url = URI(agent.get(playlist_url).body.scan(/http.*/).flatten.first)
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
# choice the max size element from multi-part m3u8 file
|
|
61
|
+
m3u8 = agent.get(m3u8_url).body.split("EXT-X-KEY").sort{|m,n|m.size <=> n.size}.last
|
|
62
|
+
|
|
61
63
|
key_url = m3u8.scan(/URI="(.*)"/).flatten.first
|
|
62
64
|
|
|
63
65
|
tses = m3u8.scan(/^.*ts_.*\.ts/)
|
data/lib/himalaya.rb
CHANGED
|
@@ -8,7 +8,7 @@ class Himalaya < WebRadio
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def download
|
|
11
|
-
html = open(@url).read
|
|
11
|
+
html = URI.open(@url).read
|
|
12
12
|
json_str = html.scan(/__NEXT_DATA__ = (.*)/).flatten.first
|
|
13
13
|
json = JSON.parse(json_str)
|
|
14
14
|
tracks = json['props']['seo']['albumData']['data']['tracks']['list']
|
|
@@ -22,7 +22,7 @@ class Himalaya < WebRadio
|
|
|
22
22
|
mp3_file = "#{@label}##{serial}.mp3"
|
|
23
23
|
mp3nize(m4a_file, mp3_file) do
|
|
24
24
|
open(m4a_file, 'wb:ASCII-8BIT') do |m4a|
|
|
25
|
-
m4a.write(open(m4a_url).read)
|
|
25
|
+
m4a.write(URI.open(m4a_url).read)
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
end
|
data/lib/hls.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'webradio'
|
|
2
|
+
require 'open3'
|
|
3
|
+
|
|
4
|
+
class HLS < WebRadio
|
|
5
|
+
private
|
|
6
|
+
def hls_download(name, serial, m3u_meta)
|
|
7
|
+
mp4_file = "#{name}##{serial}.mp4"
|
|
8
|
+
mp3_file = "#{name}##{serial}.mp3"
|
|
9
|
+
m3u = URI.open(m3u_meta, &:read).scan(/^[^#].*/).first
|
|
10
|
+
m3u = Pathname(m3u_meta).dirname.join(m3u) if Pathname(m3u).relative?
|
|
11
|
+
mp3nize(mp4_file, mp3_file) do
|
|
12
|
+
result = Open3.capture3(%Q[ffmpeg -loglevel error -protocol_whitelist file,http,https,tcp,tls,crypto -i "#{m3u}" "#{mp4_file}"])
|
|
13
|
+
unless result[2].to_i == 0
|
|
14
|
+
raise WebRadio::DownloadError.new("failed download the radio program (#{@label}).")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
data/lib/nicovideo.rb
CHANGED
|
@@ -39,7 +39,7 @@ class Nicovideo < WebRadio
|
|
|
39
39
|
_, err, status = Open3.capture3("youtube-dl -f mp4 -o #{@file} --netrc #{video.url}")
|
|
40
40
|
break if status == 0
|
|
41
41
|
next if err =~ /403: Forbidden/
|
|
42
|
-
raise ForbiddenError.new("Could not access to #{video.url}") if err =~ /TypeError/
|
|
42
|
+
raise ForbiddenError.new("Could not access to #{video.url}") if err =~ /TypeError|AssertionError/
|
|
43
43
|
raise DownloadError.new(err)
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -74,11 +74,11 @@ private
|
|
|
74
74
|
video_url = nil
|
|
75
75
|
begin
|
|
76
76
|
begin
|
|
77
|
-
rss = RSS::Parser.parse(open(list_url).read)
|
|
77
|
+
rss = RSS::Parser.parse(URI.open(list_url).read)
|
|
78
78
|
item = rss.items[offset]
|
|
79
79
|
video_url = item.link
|
|
80
80
|
rescue RSS::NotWellFormedError
|
|
81
|
-
html = open(list_url, &:read)
|
|
81
|
+
html = URI.open(list_url, &:read)
|
|
82
82
|
url = html.scan(%r|/watch/[\w]+|)[offset]
|
|
83
83
|
raise WebRadio::DownloadError.new('video not found in this pege') unless url
|
|
84
84
|
video_url = "http://www.nicovideo.jp#{url}"
|
|
@@ -93,7 +93,7 @@ private
|
|
|
93
93
|
end
|
|
94
94
|
|
|
95
95
|
def thumbinfo(video, elem = nil)
|
|
96
|
-
xml = open("http://ext.nicovideo.jp/api/getthumbinfo/#{video.id}").read
|
|
96
|
+
xml = URI.open("http://ext.nicovideo.jp/api/getthumbinfo/#{video.id}").read
|
|
97
97
|
if elem
|
|
98
98
|
return xml.scan(%r|<#{elem}>(.*)</#{elem}>|m).flatten.first
|
|
99
99
|
else
|
data/lib/onsen.rb
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
|
-
require '
|
|
3
|
+
require 'hls'
|
|
4
4
|
require 'nokogiri'
|
|
5
|
+
require 'json'
|
|
5
6
|
|
|
6
|
-
class Onsen <
|
|
7
|
+
class Onsen < HLS
|
|
7
8
|
def initialize(params, options)
|
|
8
9
|
super
|
|
9
|
-
@cover = "//*[@
|
|
10
|
+
@cover = "//*[@class='newest-content--left']//img[1]/@src" unless @cover
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def download
|
|
13
|
-
|
|
14
|
+
html = URI.open(@url, &:read)
|
|
15
|
+
serial = Nokogiri(html).css('.play-video-info td')[0].text.scan(/\d+/)[0].to_i
|
|
16
|
+
m3u8 = JSON.parse(html.scan(%r|streaming_url:("https:.*?.m3u8")|).flatten.sort.last)
|
|
17
|
+
hls_download(@label, serial, m3u8)
|
|
14
18
|
end
|
|
15
19
|
|
|
16
20
|
def dump
|
|
17
21
|
tag = Pathname(@url).basename.to_s.gsub(%r|[-/]|, '_')
|
|
18
|
-
html = Nokogiri(open(@url, &:read))
|
|
19
|
-
title = html.css('
|
|
22
|
+
html = Nokogiri(URI.open(@url, &:read))
|
|
23
|
+
title = html.css('h3')[0].text
|
|
20
24
|
return {
|
|
21
25
|
tag => {
|
|
22
26
|
'desc' => title,
|
|
@@ -25,24 +29,4 @@ class Onsen < WebRadio
|
|
|
25
29
|
}
|
|
26
30
|
}
|
|
27
31
|
end
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
def onsen_download(name, program_id)
|
|
31
|
-
html = Nokogiri(open('http://onsen.ag/', 'User-Agent' => 'iPhone', &:read))
|
|
32
|
-
begin
|
|
33
|
-
serial = html.css("##{program_id}").text.scan(/#(\d+)/).flatten.first
|
|
34
|
-
mp3_url = html.css('form[target=_self]').select {|form|
|
|
35
|
-
form.attr('action') =~ %r|/#{program_id}\w+\.mp[34]|
|
|
36
|
-
}.first.attr('action')
|
|
37
|
-
rescue NoMethodError
|
|
38
|
-
raise NotFoundError.new("no radio program in #{program_id}.")
|
|
39
|
-
end
|
|
40
|
-
src_file = "#{name}##{serial}#{mp3_url.scan(/\.mp[34]$/).first}"
|
|
41
|
-
mp3_file = "#{name}##{serial}.mp3"
|
|
42
|
-
mp3nize(src_file, mp3_file, false) do
|
|
43
|
-
open(src_file, 'wb:ASCII-8BIT') do |mp3|
|
|
44
|
-
mp3.write open(mp3_url, 'rb:ASCII-8BIT', &:read)
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
32
|
end
|
data/lib/podcast.rb
CHANGED
data/lib/standfm.rb
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'open-uri'
|
|
2
|
+
require 'nokogiri'
|
|
3
|
+
require 'mechanize'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
class StandFm < WebRadio
|
|
7
|
+
def initialize(params, options)
|
|
8
|
+
super
|
|
9
|
+
@offset = 0
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def download
|
|
13
|
+
uri = URI(@url)
|
|
14
|
+
html = Nokogiri(uri.open.read)
|
|
15
|
+
episode = uri + html.css('#root a[href^="/episodes/"]').map{|e|e.attr('href')}.uniq[@offset]
|
|
16
|
+
|
|
17
|
+
html = episode.open.read
|
|
18
|
+
json = JSON.parse(html.scan(%r[<script>window.__SERVER_STATE__=(.*)</script>]).flatten[0])
|
|
19
|
+
m4a = json['topics'].find{|k,v|v['episodeId'] == File.basename(episode.path)}.last['downloadUrl']
|
|
20
|
+
|
|
21
|
+
doc = Nokogiri(html)
|
|
22
|
+
serial = doc.title.scan(/#(\d+)/).flatten.first.to_i
|
|
23
|
+
@cover = doc.css('meta[property="og:image"]').attr('content').text unless @cover
|
|
24
|
+
m4a_file = "#{@label}##{serial}.m4a"
|
|
25
|
+
mp3_file = "#{@label}##{serial}.mp3"
|
|
26
|
+
|
|
27
|
+
mp3nize(m4a_file, mp3_file) do
|
|
28
|
+
open(m4a_file, 'wb:ASCII-8BIT') do |w|
|
|
29
|
+
w.write(URI(m4a).open.read)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def dump
|
|
35
|
+
uri = URI(@url)
|
|
36
|
+
tag = File.basename(uri.path)
|
|
37
|
+
html = Nokogiri(uri.open.read)
|
|
38
|
+
label, = html.css('title').text.split(/ \| /)
|
|
39
|
+
cover = html.css('meta[property="og:image"]').attr('content').text
|
|
40
|
+
return {
|
|
41
|
+
tag => {
|
|
42
|
+
'desc' => label,
|
|
43
|
+
'url' => @url,
|
|
44
|
+
'label' => label,
|
|
45
|
+
'cover' => cover
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
def find_player(url)
|
|
52
|
+
programs = Nokogiri(URI.open(url).read)
|
|
53
|
+
programs.css('.list-main-product a.wrap').each do |program|
|
|
54
|
+
begin
|
|
55
|
+
return Nokogiri(URI.open("https://asobistore.jp#{program.attr('href')}").read).css('iframe').last.attr('src')
|
|
56
|
+
rescue # access denied because only access by premium members
|
|
57
|
+
next
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
raise StandardError.new('movie not found.')
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/webradio.rb
CHANGED
|
@@ -14,7 +14,7 @@ class WebRadio
|
|
|
14
14
|
when %r[^https?://hibiki-radio\.jp/]
|
|
15
15
|
require 'hibiki'
|
|
16
16
|
Hibiki.new(params, options)
|
|
17
|
-
when %r[^
|
|
17
|
+
when %r[^https?://(www\.)?onsen\.ag/program/]
|
|
18
18
|
require 'onsen'
|
|
19
19
|
Onsen.new(params, options)
|
|
20
20
|
when %r[nicovideo\.jp]
|
|
@@ -29,6 +29,9 @@ class WebRadio
|
|
|
29
29
|
when %r[^https://asobistore\.jp/]
|
|
30
30
|
require 'asobistore'
|
|
31
31
|
AsobiStore.new(params, options)
|
|
32
|
+
when %r[^https://stand\.fm/channels/]
|
|
33
|
+
require 'standfm'
|
|
34
|
+
StandFm.new(params, options)
|
|
32
35
|
else
|
|
33
36
|
raise UnsupportError, "unsupported url: #{params['url']}"
|
|
34
37
|
end
|
|
@@ -132,13 +135,13 @@ private
|
|
|
132
135
|
end
|
|
133
136
|
|
|
134
137
|
def cover_image_as_url
|
|
135
|
-
open(@cover, 'rb', &:read)
|
|
138
|
+
URI.open(@cover, 'rb', &:read)
|
|
136
139
|
end
|
|
137
140
|
|
|
138
141
|
def cover_image_as_xpath
|
|
139
|
-
html = Nokogiri(open(@url, &:read))
|
|
142
|
+
html = Nokogiri(URI.open(@url, &:read))
|
|
140
143
|
image_url = (URI(@url) + (html.xpath(@cover)[0].text)).to_s
|
|
141
|
-
open(image_url, 'r:ASCII-8BIT', &:read)
|
|
144
|
+
URI.open(image_url, 'r:ASCII-8BIT', &:read)
|
|
142
145
|
end
|
|
143
146
|
|
|
144
147
|
def exist?(dst)
|
data/rget.gemspec
CHANGED
|
@@ -4,10 +4,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = "rget"
|
|
7
|
-
spec.version = "4.
|
|
8
|
-
spec.authors = ["
|
|
7
|
+
spec.version = "4.10.0"
|
|
8
|
+
spec.authors = ["Tada, Tadashi"]
|
|
9
9
|
spec.email = ["t@tdtds.jp"]
|
|
10
|
-
spec.description = %q{Downloading newest radio programs on the web. Supported radio stations are hibiki, onsen, niconico,
|
|
10
|
+
spec.description = %q{Downloading newest radio programs on the web. Supported radio stations are hibiki, onsen, niconico, himalaya, asobi store and stand.fm.}
|
|
11
11
|
spec.summary = %q{Downloading newest radio programs on the web.}
|
|
12
12
|
spec.homepage = "https://github.com/wasamas/rget"
|
|
13
13
|
spec.license = "GPL"
|
data/rget.yaml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
options:
|
|
2
|
-
mp3nize: ffmpeg -i '$1' -vn -acodec copy '$2' || ffmpeg -i '$1' -vn -y -ab
|
|
2
|
+
mp3nize: ffmpeg -i '$1' -vn -acodec copy '$2' || ffmpeg -i '$1' -vn -y -ab 128k '$2'
|
|
3
3
|
programs:
|
|
4
4
|
imas_cg:
|
|
5
5
|
desc: デレラジ☆
|
|
@@ -15,44 +15,15 @@ programs:
|
|
|
15
15
|
url: http://ch.nicovideo.jp/search/%E6%B4%B2%E5%B4%8E%E8%A5%BF?channel_id=ch2589908&mode=s&sort=f&order=d&type=video
|
|
16
16
|
label: 洲崎西
|
|
17
17
|
cover: https://pbs.twimg.com/profile_images/836214019849506818/kXSasy1Y.jpg
|
|
18
|
-
takamori:
|
|
19
|
-
desc: 高森重戦舎 (声優バラエティ専門「声フレ」チャンネル内)
|
|
20
|
-
url: https://freshlive.tv/seifre
|
|
21
|
-
label: 高森重戦舎
|
|
22
|
-
bahamut:
|
|
23
|
-
desc: ラジオ神撃のバハムート
|
|
24
|
-
url: http://hibiki-radio.jp/description/bahamut
|
|
25
|
-
label: バハムート
|
|
26
|
-
ssc:
|
|
27
|
-
desc: 春佳・彩花のSSちゃんねる
|
|
28
|
-
url: http://ch.nicovideo.jp/search/SS%E3%81%A1%E3%82%83%E3%82%93%E3%81%AD%E3%82%8B?channel_id=ch2589908&mode=s&sort=f&order=d&type=video
|
|
29
|
-
label: 春佳・彩花のSSちゃんねる
|
|
30
|
-
remake:
|
|
31
|
-
desc: りめいく!
|
|
32
|
-
url: http://ch.nicovideo.jp/search/%E3%82%8A%E3%82%81%E3%81%84%E3%81%8F?channel_id=ch2589908&mode=s&sort=f&order=d&type=video
|
|
33
|
-
label: りめいく!
|
|
34
|
-
adlib:
|
|
35
|
-
desc: あどりぶ
|
|
36
|
-
url: http://ch.nicovideo.jp/search/%E3%81%82%E3%81%A9%E3%82%8A%E3%81%B6?channel_id=ch2589908&mode=s&sort=f&order=d&type=video
|
|
37
|
-
label: あどりぶ
|
|
38
|
-
trysail:
|
|
39
|
-
desc: TrySailのTRYangle harmony
|
|
40
|
-
url: http://ch.nicovideo.jp/search/TrySail?channel_id=ch2585696&mode=s&sort=f&order=d&type=video
|
|
41
|
-
label: トラハモ
|
|
42
|
-
banpresto:
|
|
43
|
-
desc: アイドルマスターWebラジオ~バンプレストスペシャル~
|
|
44
|
-
url: http://ch.nicovideo.jp/search/IDOLM@STER?channel_id=ch346&mode=s&sort=f&order=d&type=video
|
|
45
|
-
label: バンプレスト
|
|
46
|
-
rebuild:
|
|
47
|
-
desc: Rebuild.fm
|
|
48
|
-
url: http://feeds.rebuild.fm/rebuildfm
|
|
49
|
-
label: rebuildfm
|
|
50
|
-
podcast: true
|
|
51
|
-
taneharu:
|
|
52
|
-
desc: ラジオ「たねさんはるさんとはなそ。(意志)」
|
|
53
|
-
url: http://m.himalaya.fm/jp/podcast/190452
|
|
54
|
-
label: たねさんはるさんとはなそ。(意志)
|
|
55
18
|
shiny_radio:
|
|
56
19
|
desc: ラジオ「アイドルマスター シャイニーカラーズ はばたきラジオステーション」
|
|
57
20
|
url: https://asobistore.jp/special/List?ip_seq%5B%5D=1&ip_seq%5B%5D=14&tag_seq%5B%5D=1
|
|
58
21
|
label: シャニラジ
|
|
22
|
+
luminesan:
|
|
23
|
+
desc: ル美子さん
|
|
24
|
+
url: https://ch.nicovideo.jp/luminesan/video?rss=2.0
|
|
25
|
+
label: ル美子さん
|
|
26
|
+
ranacha:
|
|
27
|
+
desc: らなちゃのひとりごと
|
|
28
|
+
url: https://stand.fm/channels/5f7d9011f04555115d2170ef
|
|
29
|
+
label: らなちゃのひとりごと
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rget
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- Tada, Tadashi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-12-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|
|
@@ -151,16 +151,18 @@ dependencies:
|
|
|
151
151
|
- !ruby/object:Gem::Version
|
|
152
152
|
version: '0'
|
|
153
153
|
description: Downloading newest radio programs on the web. Supported radio stations
|
|
154
|
-
are hibiki, onsen, niconico,
|
|
154
|
+
are hibiki, onsen, niconico, himalaya, asobi store and stand.fm.
|
|
155
155
|
email:
|
|
156
156
|
- t@tdtds.jp
|
|
157
157
|
executables:
|
|
158
158
|
- add_mp3info
|
|
159
|
+
- hls-dl
|
|
159
160
|
- rget
|
|
160
|
-
- ulizadl
|
|
161
161
|
extensions: []
|
|
162
162
|
extra_rdoc_files: []
|
|
163
163
|
files:
|
|
164
|
+
- ".devcontainer/Dockerfile"
|
|
165
|
+
- ".devcontainer/devcontainer.json"
|
|
164
166
|
- ".gitignore"
|
|
165
167
|
- ".travis.yml"
|
|
166
168
|
- Gemfile
|
|
@@ -168,17 +170,18 @@ files:
|
|
|
168
170
|
- README.md
|
|
169
171
|
- Rakefile
|
|
170
172
|
- bin/add_mp3info
|
|
173
|
+
- bin/hls-dl
|
|
171
174
|
- bin/rget
|
|
172
|
-
- bin/ulizadl
|
|
173
175
|
- lib/asobistore.rb
|
|
174
176
|
- lib/dropbox.rb
|
|
175
177
|
- lib/freshlive.rb
|
|
176
178
|
- lib/hibiki.rb
|
|
177
179
|
- lib/himalaya.rb
|
|
180
|
+
- lib/hls.rb
|
|
178
181
|
- lib/nicovideo.rb
|
|
179
182
|
- lib/onsen.rb
|
|
180
183
|
- lib/podcast.rb
|
|
181
|
-
- lib/
|
|
184
|
+
- lib/standfm.rb
|
|
182
185
|
- lib/webradio.rb
|
|
183
186
|
- rget.gemspec
|
|
184
187
|
- rget.yaml
|
|
@@ -201,8 +204,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
201
204
|
- !ruby/object:Gem::Version
|
|
202
205
|
version: '0'
|
|
203
206
|
requirements: []
|
|
204
|
-
|
|
205
|
-
rubygems_version: 2.7.7
|
|
207
|
+
rubygems_version: 3.0.8
|
|
206
208
|
signing_key:
|
|
207
209
|
specification_version: 4
|
|
208
210
|
summary: Downloading newest radio programs on the web.
|
data/bin/ulizadl
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
require "uri"
|
|
3
|
-
require "mechanize"
|
|
4
|
-
require 'open-uri'
|
|
5
|
-
require 'thread'
|
|
6
|
-
|
|
7
|
-
if ARGV.size != 2
|
|
8
|
-
$stderr.puts "download TS file decoded by Uliza style video streaming"
|
|
9
|
-
$stderr.puts "ulizadl <playlist.m3u8> <output(.ts)>"
|
|
10
|
-
exit 1
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
m3u8 = ARGV.shift
|
|
14
|
-
ts_file = "#{ARGV.shift}.ts"
|
|
15
|
-
ts_file = nil if File.basename(ts_file) == "-.ts"
|
|
16
|
-
|
|
17
|
-
begin
|
|
18
|
-
agent = Mechanize.new
|
|
19
|
-
agent.user_agent_alias = 'Windows Chrome'
|
|
20
|
-
agent.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
21
|
-
agent.get(m3u8)
|
|
22
|
-
body = agent.page.body
|
|
23
|
-
rescue ArgumentError
|
|
24
|
-
body = open(m3u8, &:read)
|
|
25
|
-
end
|
|
26
|
-
tses = body.scan(/.*\.ts.*/)
|
|
27
|
-
key_url = body.scan(/URI="(.*)"/).flatten.first
|
|
28
|
-
|
|
29
|
-
if key_url
|
|
30
|
-
key = agent.get_file(key_url)
|
|
31
|
-
decoder = OpenSSL::Cipher.new('aes-128-cbc')
|
|
32
|
-
decoder.key = key
|
|
33
|
-
decoder.decrypt
|
|
34
|
-
else
|
|
35
|
-
decoder = ''
|
|
36
|
-
def decoder.update(s); return s; end
|
|
37
|
-
def decoder.final(); return ''; end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
q = Queue.new
|
|
41
|
-
tses.each_with_index{|ts_url, block_no| q.push([ts_url, block_no])}
|
|
42
|
-
q.close
|
|
43
|
-
results = [nil] * q.size
|
|
44
|
-
|
|
45
|
-
msgs = Queue.new
|
|
46
|
-
stats = [0] * 3
|
|
47
|
-
stats.size.times do |thread_no|
|
|
48
|
-
Thread.start(thread_no) do |thread_no|
|
|
49
|
-
while (ts_url, block_no = q.pop)
|
|
50
|
-
begin
|
|
51
|
-
sleep(rand(0.1) * 3)
|
|
52
|
-
results[block_no] = agent.get_file(ts_url)
|
|
53
|
-
stats[thread_no] = block_no
|
|
54
|
-
rescue Mechanize::ResponseCodeError => e
|
|
55
|
-
case e.response_code
|
|
56
|
-
when "403" # Forbidden
|
|
57
|
-
msgs.push "T#{thread_no}: 403 Forbidden"
|
|
58
|
-
retry
|
|
59
|
-
when "429" # TooManyRequests
|
|
60
|
-
msgs.push "T#{thread_no}: 429 TooManyRequests"
|
|
61
|
-
retry
|
|
62
|
-
else
|
|
63
|
-
msgs.push "T#{thread_no}: #{e}"
|
|
64
|
-
retry # TODO: catch other status code
|
|
65
|
-
end
|
|
66
|
-
rescue
|
|
67
|
-
msgs.push "T#{thread_no}: #{e.class} #{e}"
|
|
68
|
-
break
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def print_status(block, tses, stats, msgs)
|
|
75
|
-
$stderr.puts msgs.pop while !msgs.empty?
|
|
76
|
-
$stderr.print "#{block+1}/#{tses.size} saved --"
|
|
77
|
-
stats.each_with_index{|block_no, thread_no| $stderr.print " T#{thread_no+1}:#{block_no+1}"}
|
|
78
|
-
$stderr.print "\r"
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def save(fd, results, tses, stats, msgs, decoder)
|
|
82
|
-
results.each_with_index do |data, i|
|
|
83
|
-
print_status(i, tses, stats, msgs)
|
|
84
|
-
loop do
|
|
85
|
-
if data
|
|
86
|
-
fd.write(decoder.update(data))
|
|
87
|
-
results[i] = nil # be saved data want to GC
|
|
88
|
-
break
|
|
89
|
-
else
|
|
90
|
-
sleep(1.0) # wait for complete of chunk download finished
|
|
91
|
-
data = results[i]
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
fd.write(decoder.final)
|
|
96
|
-
puts
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
if ts_file
|
|
100
|
-
open(ts_file, 'wb:ASCII-8BIT'){|f|save(f, results, tses, stats, msgs, decoder)}
|
|
101
|
-
else
|
|
102
|
-
save($stdout, results, tses, stats, msgs, decoder)
|
|
103
|
-
end
|
data/lib/uliza.rb
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
require 'webradio'
|
|
2
|
-
|
|
3
|
-
class Uliza < WebRadio
|
|
4
|
-
private
|
|
5
|
-
def uliza_download(name, html, serial_pattern, m3u_pattern)
|
|
6
|
-
serial = html.scan(serial_pattern).flatten.sort{|a,b| a.to_i <=> b.to_i}.last
|
|
7
|
-
@m4a_file = "#{name}##{serial}.m4a"
|
|
8
|
-
@mp3_file = @m4a_file.sub(/\.m4a$/, '.mp3')
|
|
9
|
-
mp3nize(@m4a_file, @mp3_file) do
|
|
10
|
-
m3u_meta2 = html.scan(m3u_pattern).flatten.sort.last
|
|
11
|
-
unless m3u_meta2
|
|
12
|
-
raise WebRadio::DownloadError.new("recent radio program not found.")
|
|
13
|
-
end
|
|
14
|
-
m3u_meta1 = open(m3u_meta2, &:read)
|
|
15
|
-
m3u = m3u_meta1.scan(/^[^#].*/).first
|
|
16
|
-
save_m4a(URI(m3u), @m4a_file)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def get_m4a(uri_playlist)
|
|
21
|
-
open(uri_playlist).each_line do |l|
|
|
22
|
-
next if /^#/ =~ l
|
|
23
|
-
l.chomp!
|
|
24
|
-
print "."
|
|
25
|
-
yield open(uri_playlist + l, 'r:ASCII-8BIT', &:read)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def save_m4a(uri_playlist, m4a_file)
|
|
30
|
-
open(m4a_file, 'wb:ASCII-8BIT') do |m4a|
|
|
31
|
-
get_m4a(uri_playlist) do |part|
|
|
32
|
-
m4a.write part
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|