rget 4.10.0 → 4.13.1
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 +2 -1
- data/lib/hls.rb +4 -3
- data/lib/nicovideo.rb +1 -1
- data/lib/onsen.rb +7 -3
- data/lib/webradio.rb +6 -3
- data/lib/youtube.rb +121 -0
- data/rget.gemspec +3 -2
- data/rget.yaml +8 -1
- metadata +19 -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: 61e6f81e8c57cc0ffdee5a2455b37a22d9eb012b07ac0106faba68c5a628a063
|
|
4
|
+
data.tar.gz: dc9c5a901981b16a765664062ba3fe135d9bb46cbaa56f49b7637902d705aba2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a976800fbf484379d64b9728e8d5feae3a16ffa04ec8d8298f74ed3914d0022d1ea9a6f19cb8d888cafed34d68df99916f02572adf4c44d0ecda8756c202ff05
|
|
7
|
+
data.tar.gz: 7536109c0076f84727db67e00ae36cdabb82219413a2d3fc928501ced572efc06d978557056547634642721883f41efc4790b67eb129ff441745d2e009f379f0
|
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,9 +8,10 @@ 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 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/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
|
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)
|
|
@@ -32,6 +29,12 @@ class WebRadio
|
|
|
32
29
|
when %r[^https://stand\.fm/channels/]
|
|
33
30
|
require 'standfm'
|
|
34
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)
|
|
35
38
|
else
|
|
36
39
|
raise UnsupportError, "unsupported url: #{params['url']}"
|
|
37
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.
|
|
7
|
+
spec.version = "4.13.1"
|
|
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
|
|
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
|
|
@@ -27,3 +26,11 @@ programs:
|
|
|
27
26
|
desc: らなちゃのひとりごと
|
|
28
27
|
url: https://stand.fm/channels/5f7d9011f04555115d2170ef
|
|
29
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.1
|
|
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-09 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
|
|
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
|
|
@@ -183,6 +196,7 @@ files:
|
|
|
183
196
|
- lib/podcast.rb
|
|
184
197
|
- lib/standfm.rb
|
|
185
198
|
- lib/webradio.rb
|
|
199
|
+
- lib/youtube.rb
|
|
186
200
|
- rget.gemspec
|
|
187
201
|
- rget.yaml
|
|
188
202
|
homepage: https://github.com/wasamas/rget
|
|
@@ -204,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
204
218
|
- !ruby/object:Gem::Version
|
|
205
219
|
version: '0'
|
|
206
220
|
requirements: []
|
|
207
|
-
rubygems_version: 3.
|
|
221
|
+
rubygems_version: 3.3.4
|
|
208
222
|
signing_key:
|
|
209
223
|
specification_version: 4
|
|
210
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
|