rget 4.9.3 → 4.13.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 +1 -1
- data/.devcontainer/devcontainer.json +4 -1
- data/.travis.yml +1 -1
- data/README.md +3 -2
- data/lib/hls.rb +4 -3
- data/lib/onsen.rb +7 -3
- data/lib/standfm.rb +62 -0
- data/lib/webradio.rb +9 -3
- data/lib/youtube.rb +121 -0
- data/rget.gemspec +4 -3
- data/rget.yaml +12 -1
- metadata +21 -6
- data/lib/freshlive.rb +0 -113
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 680138673b4a74e24fc3114cfefd7e4d7a1f4f56fe37ca70b7848c0256b95d63
|
|
4
|
+
data.tar.gz: fe7fdd1168af355285b5ec7ab4c9741f1acdd4fc496ea3a2bc9e59b5869f7011
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 394aac18d5099e78ef52c99896a132fdc805edc8670fa7e980e074ffd8ecd7fd02cf6ae423c059852c43253ab585b82d720d1400602c127a6dbd46ac80f3474e
|
|
7
|
+
data.tar.gz: d01d41849aa9e3ac8cef2e3e523b62445116bffe09952d7f91909a82c2e1b7d47de58429d537eb423a35c563ebe84ce29eecbdd42ed26c07629b0ee2a346fc88
|
data/.devcontainer/Dockerfile
CHANGED
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
|
|
14
14
|
// Add the IDs of extensions you want installed when the container is created.
|
|
15
15
|
"extensions": [
|
|
16
|
-
|
|
16
|
+
"rebornix.ruby"
|
|
17
|
+
,"github.vscode-pull-request-github"
|
|
18
|
+
,"eamodio.gitlens"
|
|
19
|
+
,"donjayamanne.githistory"
|
|
17
20
|
],
|
|
18
21
|
|
|
19
22
|
// Uncomment the next line if you want start specific services in your Docker Compose config.
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
|
@@ -5,12 +5,13 @@ 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
|
+
* YouTube (playlist or channel)
|
|
11
12
|
|
|
12
13
|
If you want to save files as MP3, needs `ffmpeg` command.
|
|
13
|
-
To download niconico video, also needs latest `youtube-dl` command (you can get it from `https://yt-dl.org/`), then specify
|
|
14
|
+
To download niconico video or YouTube, also needs latest `youtube-dl` command (you can get it from `https://yt-dl.org/`), then specify user ID and password to `~/.netrc` for niconico as:
|
|
14
15
|
|
|
15
16
|
```
|
|
16
17
|
machine niconico
|
data/lib/hls.rb
CHANGED
|
@@ -3,13 +3,14 @@ require 'open3'
|
|
|
3
3
|
|
|
4
4
|
class HLS < WebRadio
|
|
5
5
|
private
|
|
6
|
-
def hls_download(name, serial, m3u_meta)
|
|
6
|
+
def hls_download(name, serial, m3u_meta, headers = {})
|
|
7
7
|
mp4_file = "#{name}##{serial}.mp4"
|
|
8
8
|
mp3_file = "#{name}##{serial}.mp3"
|
|
9
|
-
m3u = URI.open(m3u_meta, &:read).scan(/^[^#].*/).first
|
|
9
|
+
m3u = URI.open(m3u_meta, headers, &:read).scan(/^[^#].*/).first
|
|
10
10
|
m3u = Pathname(m3u_meta).dirname.join(m3u) if Pathname(m3u).relative?
|
|
11
|
+
ffmpeg_headers = headers.empty? ? '' : "-headers '" + headers.map{|k, v| "#{k}: #{v}"}.join("\r\n") + "'"
|
|
11
12
|
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
|
+
result = Open3.capture3(%Q[ffmpeg -loglevel error -protocol_whitelist file,http,https,tcp,tls,crypto #{ffmpeg_headers} -i "#{m3u}" "#{mp4_file}"])
|
|
13
14
|
unless result[2].to_i == 0
|
|
14
15
|
raise WebRadio::DownloadError.new("failed download the radio program (#{@label}).")
|
|
15
16
|
end
|
data/lib/onsen.rb
CHANGED
|
@@ -5,21 +5,25 @@ require 'nokogiri'
|
|
|
5
5
|
require 'json'
|
|
6
6
|
|
|
7
7
|
class Onsen < HLS
|
|
8
|
+
HEADERS = {
|
|
9
|
+
"Referer" => 'https://www.onsen.ag/'
|
|
10
|
+
}
|
|
11
|
+
|
|
8
12
|
def initialize(params, options)
|
|
9
13
|
super
|
|
10
14
|
@cover = "//*[@class='newest-content--left']//img[1]/@src" unless @cover
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def download
|
|
14
|
-
html = URI.open(@url, &:read)
|
|
18
|
+
html = URI.open(@url, HEADERS, &:read)
|
|
15
19
|
serial = Nokogiri(html).css('.play-video-info td')[0].text.scan(/\d+/)[0].to_i
|
|
16
20
|
m3u8 = JSON.parse(html.scan(%r|streaming_url:("https:.*?.m3u8")|).flatten.sort.last)
|
|
17
|
-
hls_download(@label, serial, m3u8)
|
|
21
|
+
hls_download(@label, serial, m3u8, HEADERS)
|
|
18
22
|
end
|
|
19
23
|
|
|
20
24
|
def dump
|
|
21
25
|
tag = Pathname(@url).basename.to_s.gsub(%r|[-/]|, '_')
|
|
22
|
-
html = Nokogiri(URI.open(@url, &:read))
|
|
26
|
+
html = Nokogiri(URI.open(@url, HEADERS, &:read))
|
|
23
27
|
title = html.css('h3')[0].text
|
|
24
28
|
return {
|
|
25
29
|
tag => {
|
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
|
@@ -20,15 +20,21 @@ class WebRadio
|
|
|
20
20
|
when %r[nicovideo\.jp]
|
|
21
21
|
require 'nicovideo'
|
|
22
22
|
Nicovideo.new(params, options)
|
|
23
|
-
when %r[^https://freshlive\.tv/]
|
|
24
|
-
require 'freshlive'
|
|
25
|
-
FreshLive.new(params, options)
|
|
26
23
|
when %r[^http://m\.himalaya\.fm/]
|
|
27
24
|
require 'himalaya'
|
|
28
25
|
Himalaya.new(params, options)
|
|
29
26
|
when %r[^https://asobistore\.jp/]
|
|
30
27
|
require 'asobistore'
|
|
31
28
|
AsobiStore.new(params, options)
|
|
29
|
+
when %r[^https://stand\.fm/channels/]
|
|
30
|
+
require 'standfm'
|
|
31
|
+
StandFm.new(params, options)
|
|
32
|
+
when %r[^https://www\.youtube\.com/playlist]
|
|
33
|
+
require 'youtube'
|
|
34
|
+
YoutubePlaylist.new(params, options)
|
|
35
|
+
when %r[^https://www\.youtube\.com/c/]
|
|
36
|
+
require 'youtube'
|
|
37
|
+
YoutubeChannel.new(params, options)
|
|
32
38
|
else
|
|
33
39
|
raise UnsupportError, "unsupported url: #{params['url']}"
|
|
34
40
|
end
|
data/lib/youtube.rb
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
require 'open-uri'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'nokogiri'
|
|
4
|
+
|
|
5
|
+
class Youtube < WebRadio
|
|
6
|
+
def initialize(params, options)
|
|
7
|
+
super
|
|
8
|
+
@offset = 0
|
|
9
|
+
@target_content = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def youtube_download(mp4_url, mp4_file, mp3_file)
|
|
15
|
+
begin
|
|
16
|
+
mp3nize(mp4_file, mp3_file) do
|
|
17
|
+
loop do
|
|
18
|
+
print '.'
|
|
19
|
+
_, err, status = Open3.capture3("youtube-dl -f mp4 -o '#{mp4_file}' --netrc '#{mp4_url}'")
|
|
20
|
+
break if status == 0
|
|
21
|
+
next if err =~ /403: Forbidden/
|
|
22
|
+
raise ForbiddenError.new("Could not access to #{mp4_url}") if err =~ /TypeError|AssertionError/
|
|
23
|
+
raise DownloadError.new(err)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
rescue ForbiddenError
|
|
27
|
+
puts "#{$!.message}, try next."
|
|
28
|
+
@offset += 1
|
|
29
|
+
retry
|
|
30
|
+
rescue NotFoundError
|
|
31
|
+
raise DownloadError.new('video not found')
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def first_video(html)
|
|
36
|
+
json_str = html.scan(/ytInitialData = (.*);<\/script>/).flatten.first
|
|
37
|
+
json = JSON.parse(json_str)
|
|
38
|
+
contents = json.dig(*@target_content)
|
|
39
|
+
yield contents.first
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def serial_file(title, label, serial, ext)
|
|
43
|
+
if serial > 0
|
|
44
|
+
"#{@label}##{serial}.#{ext}"
|
|
45
|
+
else
|
|
46
|
+
"#{title}.#{ext}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class YoutubePlaylist < Youtube
|
|
52
|
+
|
|
53
|
+
def initialize(params, options)
|
|
54
|
+
super
|
|
55
|
+
@target_content = ["contents", "twoColumnBrowseResultsRenderer", "tabs", 0, "tabRenderer", "content", "sectionListRenderer", "contents", 0, "itemSectionRenderer", "contents", 0, "playlistVideoListRenderer", "contents"].freeze
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def download
|
|
59
|
+
first_video(URI.open(@url).read) do |content|
|
|
60
|
+
item = content['playlistVideoRenderer']
|
|
61
|
+
@cover = item['thumbnail']['thumbnails'].last['url']
|
|
62
|
+
title = item['title']['runs'][0]['text']
|
|
63
|
+
serial = title.scan(/\d+/).first.to_i
|
|
64
|
+
url = "https://www.youtube.com/watch?v=#{item["videoId"]}"
|
|
65
|
+
|
|
66
|
+
mp4_file = serial_file title, @label, serial, 'mp4'
|
|
67
|
+
mp3_file = serial_file title, @label, serial, 'mp3'
|
|
68
|
+
|
|
69
|
+
youtube_download url, mp4_file, mp3_file
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def dump
|
|
74
|
+
uri = URI(@url)
|
|
75
|
+
tag = Hash[URI.decode_www_form(File.basename(uri.query))]['list']
|
|
76
|
+
html = Nokogiri(uri.open.read)
|
|
77
|
+
label, = html.css('title').text.split(/ - .*\Z/)
|
|
78
|
+
return {
|
|
79
|
+
tag => {
|
|
80
|
+
'desc' => label,
|
|
81
|
+
'url' => @url,
|
|
82
|
+
'label' => label
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class YoutubeChannel < Youtube
|
|
89
|
+
def initialize(params, options)
|
|
90
|
+
super
|
|
91
|
+
@target_content = ['contents', 'twoColumnBrowseResultsRenderer', 'tabs', 1, 'tabRenderer', 'content', 'sectionListRenderer', 'contents', 0, 'itemSectionRenderer', 'contents', 0, 'gridRenderer', 'items'].freeze
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def download
|
|
95
|
+
first_video(URI.open(@url).read) do |content|
|
|
96
|
+
item = content['gridVideoRenderer']
|
|
97
|
+
@cover = item['thumbnail']['thumbnails'].last['url']
|
|
98
|
+
title = item['title']['runs'][0]['text']
|
|
99
|
+
serial = title.scan(/\d+/).first.to_i
|
|
100
|
+
url = "https://www.youtube.com#{item['navigationEndpoint']['commandMetadata']['webCommandMetadata']['url']}"
|
|
101
|
+
|
|
102
|
+
mp4_file = serial_file title, @label, serial, 'mp4'
|
|
103
|
+
mp3_file = serial_file title, @label, serial, 'mp3'
|
|
104
|
+
|
|
105
|
+
youtube_download url, mp4_file, mp3_file
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def dump
|
|
110
|
+
tag = @url.scan(%r|/c/([^/]*)|).flatten.first
|
|
111
|
+
html = Nokogiri(URI(@url).open.read)
|
|
112
|
+
label = html.css('meta[property="og:title"]').attr('content').text
|
|
113
|
+
return {
|
|
114
|
+
tag => {
|
|
115
|
+
'desc' => label,
|
|
116
|
+
'url' => "#{@url.scan(%r|(.*?/c/[^/]*)|).flatten.first}/videos?view=0&sort=dd&flow=grid",
|
|
117
|
+
'label' => label
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
end
|
|
121
|
+
end
|
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.13.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, stand.fm and youtube.}
|
|
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"
|
|
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
spec.add_runtime_dependency "pit"
|
|
24
24
|
spec.add_runtime_dependency "dropbox_api"
|
|
25
25
|
spec.add_runtime_dependency "mp3info"
|
|
26
|
+
spec.add_runtime_dependency "rss"
|
|
26
27
|
|
|
27
28
|
spec.add_development_dependency "bundler"
|
|
28
29
|
spec.add_development_dependency "rake"
|
data/rget.yaml
CHANGED
|
@@ -9,7 +9,6 @@ programs:
|
|
|
9
9
|
desc: ラジオ「松井恵理子のにじらじっ!」
|
|
10
10
|
url: http://www.onsen.ag/program/matsui/
|
|
11
11
|
label: 松井恵理子のにじらじ
|
|
12
|
-
cover: //*[@id='newProgramWrap']//img[1]/@src
|
|
13
12
|
suzakinishi:
|
|
14
13
|
desc: 洲崎西
|
|
15
14
|
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
|
|
@@ -23,3 +22,15 @@ programs:
|
|
|
23
22
|
desc: ル美子さん
|
|
24
23
|
url: https://ch.nicovideo.jp/luminesan/video?rss=2.0
|
|
25
24
|
label: ル美子さん
|
|
25
|
+
ranacha:
|
|
26
|
+
desc: らなちゃのひとりごと
|
|
27
|
+
url: https://stand.fm/channels/5f7d9011f04555115d2170ef
|
|
28
|
+
label: らなちゃのひとりごと
|
|
29
|
+
active:
|
|
30
|
+
desc: 愛美とはるかの2年A組青春アクティ部!
|
|
31
|
+
url: https://www.youtube.com/playlist?list=PLI5zma0g_n8_y5Xk5eFwX8osz4Q2a3cqU
|
|
32
|
+
label: アクティ部
|
|
33
|
+
shimotsukin:
|
|
34
|
+
desc: 霜月はるかのしもつきんチャンネル
|
|
35
|
+
url: https://www.youtube.com/c/shimotsukinchannel/videos?view=0&sort=dd&flow=grid
|
|
36
|
+
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.13.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: 2021-12-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|
|
@@ -94,6 +94,20 @@ dependencies:
|
|
|
94
94
|
- - ">="
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rss
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :runtime
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
97
111
|
- !ruby/object:Gem::Dependency
|
|
98
112
|
name: bundler
|
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -151,7 +165,7 @@ dependencies:
|
|
|
151
165
|
- !ruby/object:Gem::Version
|
|
152
166
|
version: '0'
|
|
153
167
|
description: Downloading newest radio programs on the web. Supported radio stations
|
|
154
|
-
are hibiki, onsen, niconico,
|
|
168
|
+
are hibiki, onsen, niconico, himalaya, asobi store, stand.fm and youtube.
|
|
155
169
|
email:
|
|
156
170
|
- t@tdtds.jp
|
|
157
171
|
executables:
|
|
@@ -174,14 +188,15 @@ files:
|
|
|
174
188
|
- bin/rget
|
|
175
189
|
- lib/asobistore.rb
|
|
176
190
|
- lib/dropbox.rb
|
|
177
|
-
- lib/freshlive.rb
|
|
178
191
|
- lib/hibiki.rb
|
|
179
192
|
- lib/himalaya.rb
|
|
180
193
|
- lib/hls.rb
|
|
181
194
|
- lib/nicovideo.rb
|
|
182
195
|
- lib/onsen.rb
|
|
183
196
|
- lib/podcast.rb
|
|
197
|
+
- lib/standfm.rb
|
|
184
198
|
- lib/webradio.rb
|
|
199
|
+
- lib/youtube.rb
|
|
185
200
|
- rget.gemspec
|
|
186
201
|
- rget.yaml
|
|
187
202
|
homepage: https://github.com/wasamas/rget
|
|
@@ -203,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
203
218
|
- !ruby/object:Gem::Version
|
|
204
219
|
version: '0'
|
|
205
220
|
requirements: []
|
|
206
|
-
rubygems_version: 3.
|
|
221
|
+
rubygems_version: 3.2.32
|
|
207
222
|
signing_key:
|
|
208
223
|
specification_version: 4
|
|
209
224
|
summary: Downloading newest radio programs on the web.
|
data/lib/freshlive.rb
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
require 'webradio'
|
|
2
|
-
require 'nokogiri'
|
|
3
|
-
require 'json'
|
|
4
|
-
|
|
5
|
-
class FreshLive < WebRadio
|
|
6
|
-
def download
|
|
7
|
-
if URI(@url).path =~ %r|/search/|
|
|
8
|
-
archive = @url
|
|
9
|
-
else
|
|
10
|
-
archive = URI(File.join(@url + '/programs/archive'))
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
each_programs(Nokogiri(URI.open(archive).read)) do |meta|
|
|
14
|
-
begin
|
|
15
|
-
serial = meta['data']['title'].scan(/\d+$/).first.to_i
|
|
16
|
-
src = "#{@label}##{'%02d' % serial}.ts"
|
|
17
|
-
dst = src.sub(/\.ts$/, @options.mp3 ? '.mp3' : '.mp4')
|
|
18
|
-
if exist?(dst)
|
|
19
|
-
puts "#{dst} is existent, skipped."
|
|
20
|
-
return
|
|
21
|
-
end
|
|
22
|
-
unless exist?(src)
|
|
23
|
-
open(src, 'wb') do |w|
|
|
24
|
-
print "getting #{src}..."
|
|
25
|
-
ts_list(meta['data']['archiveStreamUrl']).each_with_index do |u, i|
|
|
26
|
-
print '.' if i % 50 == 0
|
|
27
|
-
w.write(URI.open(u, 'rb').read)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
if @options.mp3
|
|
32
|
-
dst = to_mp3(src)
|
|
33
|
-
else
|
|
34
|
-
dst = to_mp4(src)
|
|
35
|
-
end
|
|
36
|
-
puts 'done.'
|
|
37
|
-
move(dst)
|
|
38
|
-
return
|
|
39
|
-
rescue OpenURI::HTTPError
|
|
40
|
-
puts 'try next.'
|
|
41
|
-
next
|
|
42
|
-
rescue
|
|
43
|
-
puts 'fail.'
|
|
44
|
-
$stderr.puts 'faild to convert .ts => .mp4'
|
|
45
|
-
return
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
puts 'fail.'
|
|
49
|
-
$stderr.puts 'free program not found.'
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def dump
|
|
53
|
-
u = URI(@url)
|
|
54
|
-
if u.path =~ %r|/search/|
|
|
55
|
-
desc = URI.decode_www_form_component(Pathname(u.path).basename.to_s)
|
|
56
|
-
return {
|
|
57
|
-
'freshlive_search' => {
|
|
58
|
-
'desc' => desc,
|
|
59
|
-
'url' => @url,
|
|
60
|
-
'label' => desc
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
else
|
|
64
|
-
tag = Pathname(u.path).basename.to_s
|
|
65
|
-
meta = JSON.parse(Nokogiri(URI.open(@url, &:read)).css('script').first)
|
|
66
|
-
return {
|
|
67
|
-
tag => {
|
|
68
|
-
'desc' => meta['name'],
|
|
69
|
-
'url' => @url,
|
|
70
|
-
'label' => tag
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
private
|
|
77
|
-
def each_programs(html)
|
|
78
|
-
x = "//section[descendant::h1[contains(text(),'アーカイブ')]]//*[contains(@class,'ProgramTitle')]/a/@href"
|
|
79
|
-
html.xpath(x).each do |href|
|
|
80
|
-
id = Pathname(href.value).basename.to_s
|
|
81
|
-
yield JSON.parse(URI.open("https://freshlive.tv/proxy/Programs;id=#{id}", &:read))
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def ts_list(rate_m3u8)
|
|
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
|
-
end
|
|
89
|
-
|
|
90
|
-
def to_mp3(src)
|
|
91
|
-
dst = src.sub(/ts$/, 'mp3')
|
|
92
|
-
command = "ffmpeg -i '#{src}' -vn -ab 64k '#{dst}'"
|
|
93
|
-
result = Open3.capture3(command)
|
|
94
|
-
if result[2].to_i == 0
|
|
95
|
-
File.delete(src)
|
|
96
|
-
else
|
|
97
|
-
File.delete(dst) if File.exist?(dst)
|
|
98
|
-
end
|
|
99
|
-
return dst
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def to_mp4(src)
|
|
103
|
-
dst = src.sub(/ts$/, 'mp4')
|
|
104
|
-
command = "ffmpeg -i '#{src}' -vcodec copy -strict -2 '#{dst}'"
|
|
105
|
-
result = Open3.capture3(command)
|
|
106
|
-
if result[2].to_i == 0
|
|
107
|
-
File.delete(src)
|
|
108
|
-
else
|
|
109
|
-
File.delete(dst) if File.exist?(dst)
|
|
110
|
-
end
|
|
111
|
-
return dst
|
|
112
|
-
end
|
|
113
|
-
end
|