rget 4.11.0 → 4.13.2
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/README.md +1 -1
- data/lib/hls.rb +4 -3
- data/lib/nicovideo.rb +2 -2
- data/lib/onsen.rb +7 -3
- data/lib/webradio.rb +4 -4
- data/lib/youtube.rb +102 -21
- data/rget.gemspec +3 -2
- data/rget.yaml +5 -2
- metadata +18 -5
- 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: bef9d92d8791dfd9e2e4cf6852a30d07bf4bcd8a46a492a443d1d7223ac7877d
|
4
|
+
data.tar.gz: d03997be575932a0cc303ba44848c5178bd3802efcccfa223507eb0e3b417165
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 208553638033e5d34e9367c7a44da64620bd8f3306b91e70d831a087607a22d5e3467a6ae769678e59ca687b7fb55539ff6b24720e0c2944976b86a2110c09b1
|
7
|
+
data.tar.gz: d7e6f651fb7c91df44f5fe0947f627bbcef767019719e4405c288bcad668eb3262178526100cb11b49a303720fb0873456c2964cb7cdd2a879ff2be1e6ba6d0c
|
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/README.md
CHANGED
@@ -8,7 +8,7 @@ Downloading newest radio programs on the web. Supported radio stations are:
|
|
8
8
|
* himalaya.fm
|
9
9
|
* Asobi Store
|
10
10
|
* stand.fm
|
11
|
-
* YouTube (playlist
|
11
|
+
* YouTube (playlist or channel)
|
12
12
|
|
13
13
|
If you want to save files as MP3, needs `ffmpeg` command.
|
14
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:
|
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/nicovideo.rb
CHANGED
@@ -31,7 +31,7 @@ class Nicovideo < WebRadio
|
|
31
31
|
serial = tmp if tmp > 0
|
32
32
|
end
|
33
33
|
appendix = title =~ /おまけ|アフタートーク/ ? 'a' : ''
|
34
|
-
@file = "#{@label}##{'%02d' % serial}#{appendix}.#{video.type}"
|
34
|
+
@file = "#{@label}##{'%02d' % serial}#{appendix}.#{video.type || 'mp4'}"
|
35
35
|
@mp3_file = @file.sub(/\....$/, '.mp3')
|
36
36
|
mp3nize(@file, @mp3_file) do
|
37
37
|
loop do
|
@@ -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|AssertionError/
|
42
|
+
raise ForbiddenError.new("Could not access to #{video.url}") if err =~ /TypeError|AssertionError|The video can't be downloaded/
|
43
43
|
raise DownloadError.new(err)
|
44
44
|
end
|
45
45
|
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/webradio.rb
CHANGED
@@ -20,9 +20,6 @@ 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)
|
@@ -34,7 +31,10 @@ class WebRadio
|
|
34
31
|
StandFm.new(params, options)
|
35
32
|
when %r[^https://www\.youtube\.com/playlist]
|
36
33
|
require 'youtube'
|
37
|
-
|
34
|
+
YoutubePlaylist.new(params, options)
|
35
|
+
when %r[^https://www\.youtube\.com/c/]
|
36
|
+
require 'youtube'
|
37
|
+
YoutubeChannel.new(params, options)
|
38
38
|
else
|
39
39
|
raise UnsupportError, "unsupported url: #{params['url']}"
|
40
40
|
end
|
data/lib/youtube.rb
CHANGED
@@ -1,40 +1,121 @@
|
|
1
1
|
require 'open-uri'
|
2
2
|
require 'json'
|
3
|
+
require 'nokogiri'
|
3
4
|
|
4
5
|
class Youtube < WebRadio
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
def initialize(params, options)
|
7
|
+
super
|
8
|
+
@offset = 0
|
9
|
+
@target_content = []
|
10
|
+
end
|
9
11
|
|
10
|
-
|
11
|
-
html = URI.open(@url).read
|
12
|
-
json_str = html.scan(/ytInitialData = (.*);<\/script>/).flatten.first
|
13
|
-
json = JSON.parse(json_str)
|
14
|
-
title = json["contents"]["twoColumnBrowseResultsRenderer"]["tabs"][0]["tabRenderer"]["content"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]["contents"][0]["playlistVideoListRenderer"]["contents"][0]["playlistVideoRenderer"]["title"]["runs"][0]["text"]
|
15
|
-
@cover = json["contents"]["twoColumnBrowseResultsRenderer"]["tabs"][0]["tabRenderer"]["content"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]["contents"][0]["playlistVideoListRenderer"]["contents"][0]["playlistVideoRenderer"]["thumbnail"]["thumbnails"].last["url"]
|
16
|
-
mp4_url = "https://www.youtube.com#{json["contents"]["twoColumnBrowseResultsRenderer"]["tabs"][0]["tabRenderer"]["content"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]["contents"][0]["playlistVideoListRenderer"]["contents"][0]["playlistVideoRenderer"]["navigationEndpoint"]["commandMetadata"]["webCommandMetadata"]["url"]}"
|
17
|
-
serial = title.scan(/[0-9]+/).first
|
12
|
+
private
|
18
13
|
|
19
|
-
|
20
|
-
mp3_file = "#{@label}##{serial}.mp3"
|
14
|
+
def youtube_download(mp4_url, mp4_file, mp3_file)
|
21
15
|
begin
|
22
16
|
mp3nize(mp4_file, mp3_file) do
|
23
17
|
loop do
|
24
18
|
print '.'
|
25
|
-
_, err, status = Open3.capture3("youtube-dl -f mp4 -o #{mp4_file} --netrc '#{mp4_url}'")
|
19
|
+
_, err, status = Open3.capture3("youtube-dl -f mp4 -o '#{mp4_file}' --netrc '#{mp4_url}'")
|
26
20
|
break if status == 0
|
27
21
|
next if err =~ /403: Forbidden/
|
28
22
|
raise ForbiddenError.new("Could not access to #{mp4_url}") if err =~ /TypeError|AssertionError/
|
29
23
|
raise DownloadError.new(err)
|
30
24
|
end
|
31
25
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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}"
|
38
47
|
end
|
39
48
|
end
|
40
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.
|
7
|
+
spec.version = "4.13.2"
|
8
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, himalaya, asobi store, stand.fm and youtube
|
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
|
@@ -30,4 +29,8 @@ programs:
|
|
30
29
|
active:
|
31
30
|
desc: 愛美とはるかの2年A組青春アクティ部!
|
32
31
|
url: https://www.youtube.com/playlist?list=PLI5zma0g_n8_y5Xk5eFwX8osz4Q2a3cqU
|
33
|
-
label: アクティ部
|
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.2
|
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: 2022-02-15 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, himalaya, asobi store, stand.fm and youtube
|
168
|
+
are hibiki, onsen, niconico, himalaya, asobi store, stand.fm and youtube.
|
155
169
|
email:
|
156
170
|
- t@tdtds.jp
|
157
171
|
executables:
|
@@ -174,7 +188,6 @@ 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
|
@@ -205,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
205
218
|
- !ruby/object:Gem::Version
|
206
219
|
version: '0'
|
207
220
|
requirements: []
|
208
|
-
rubygems_version: 3.
|
221
|
+
rubygems_version: 3.3.4
|
209
222
|
signing_key:
|
210
223
|
specification_version: 4
|
211
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
|