nicoloid 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'termcolor'
4
+
5
+ # Specify your gem's dependencies in nicoloid.gemspec
6
+ gemspec
data/README.mkd ADDED
@@ -0,0 +1,95 @@
1
+ # Nicoloid - Download video from nicovideo and convert to mp3
2
+
3
+ __OWN YOUR RISK__
4
+
5
+ ## Note
6
+
7
+ * This tool is optimized for VOCALOID.
8
+ * If video name contains VOCALOID names, this tool will put to artist tag in ID3.
9
+
10
+ ## Requirement
11
+
12
+ * Ruby 1.9+ (Ruby 1.9.2+ is supported)
13
+ * ffmpeg
14
+
15
+ ## How to use
16
+
17
+ 1. write config in yaml, format is below
18
+ 2. run `nicoloid /path/to/config.yml`
19
+
20
+ ## Configuration
21
+
22
+ account:
23
+ mail: your_mail
24
+ password: your_password
25
+
26
+ directories:
27
+ temporary: temporary_directory
28
+ output: mp3_directory
29
+
30
+ ffmpeg: path_to_ffmpeg
31
+
32
+ # if you use ranking as source
33
+ source:
34
+ from: ranking
35
+ method: ranking_method
36
+ span: ranking_span
37
+ category: ranking_category
38
+
39
+ # if you use mylist as source
40
+ # (tbd)
41
+
42
+ # the followings are optional.
43
+
44
+ proxy:
45
+ host: proxy_host
46
+ port: proxy_port
47
+
48
+ limit: limit_count_of_videos
49
+
50
+ ### Note
51
+
52
+ #### output directory
53
+
54
+ * __output directory will be wiped__ all its including files before converting. please move file which you need.
55
+ * output mp3-s will be saved in output directory.
56
+
57
+ #### Temporary directory
58
+
59
+ * it will be created automatically before converting, and deleted after converting.
60
+
61
+ #### What's a ranking_method and span?
62
+
63
+ * Niconico ranking url includes method, span and category.
64
+
65
+ http://www.nicovideo.jp/ranking/__METHOD__/__SPAN__/__CATEGORY__
66
+
67
+ Example: <http://www.nicovideo.jp/ranking/fav/daily/vocaloid>
68
+
69
+ source:
70
+ method: fav
71
+ span: daily
72
+ category: vocaloid
73
+
74
+ ## License
75
+
76
+ MIT Licence
77
+
78
+ (c) Shota Fukumori 2011
79
+
80
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
81
+ > of this software and associated documentation files (the "Software"), to deal
82
+ > in the Software without restriction, including without limitation the rights
83
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
84
+ > copies of the Software, and to permit persons to whom the Software is
85
+ > furnished to do so, subject to the following conditions:
86
+ > The above copyright notice and this permission notice shall be included in
87
+ > all copies or substantial portions of the Software.
88
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
89
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
90
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
91
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
92
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
93
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
94
+ > THE SOFTWARE.
95
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/bin/nicoloid ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: filetype=ruby
3
+
4
+ require_relative "../lib/nicoloid"
5
+
6
+ Nicoloid.run ARGV
data/lib/nicoloid.rb ADDED
@@ -0,0 +1,286 @@
1
+ #-*- coding:utf-8 -*-
2
+
3
+ # nicoloid.rb
4
+ # Author: Shota Fukumori (sora_h) <sorah@tubusu.net>
5
+ # License: MIT Licence
6
+ # The MIT License {{{
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ # THE SOFTWARE.
24
+ #}}}
25
+
26
+ require 'rubygems'
27
+ require 'niconico'
28
+ require 'yaml'
29
+ require 'fileutils'
30
+ require 'taglib'
31
+ require 'termcolor'
32
+
33
+ frame_factory = TagLib::ID3v2::FrameFactory.instance
34
+ frame_factory.default_text_encoding = TagLib::String::UTF8
35
+
36
+ class File
37
+ NULL ||= /mswin/u =~ RUBY_PLATFORM ? 'NUL' : '/dev/null'
38
+ end
39
+
40
+ class Nicoloid
41
+
42
+ VOCALOIDS = %w(初音ミク
43
+ 鏡音リン
44
+ 鏡音レン
45
+ 巡音ルカ
46
+ KAITO
47
+ MEIKO
48
+ 重音テト
49
+ GUMI
50
+ めぐっぽいど
51
+ Megpoid
52
+ がくっぽいど
53
+ 神威がくぽ
54
+ がくぽ
55
+ VY1
56
+ 歌愛ユキ
57
+ )
58
+ VOCALOIDS_ALIAS = {"Megpoid" => "GUMI",
59
+ "めぐっぽいど" => "GUMI",
60
+ "がくぽ" => "神威がくぽ",
61
+ "がくっぽいど" => "神威がくぽ",
62
+ "megpoid" => "GUMI"}
63
+
64
+ VOCALOIDS_SORTING = {"初音ミク" => "Hatsune Miku",
65
+ "鏡音リン" => "Kagamine Rin",
66
+ "鏡音レン" => "Kagamine Ren",
67
+ "巡音ルカ" => "Megurine Ruka",
68
+ "KAITO" => "Kaito",
69
+ "MEIKO" => "Meiko",
70
+ "重音テト" => "Kasane Teto",
71
+ "GUMI" => "Gumi",
72
+ "神威がくぽ" => "Kamui Gakupo",
73
+ "VY1" => "VY1",
74
+ "歌愛ユキ" => "Kaai Yuki"}
75
+
76
+ class << self
77
+ def puts_progress(str)
78
+ puts "<bold><blue> *</blue> <white>#{str}</white></bold>".termcolor
79
+ yield
80
+ end
81
+
82
+ def run(argv)
83
+ puts "Usage: #{File.basename(__FILE__)} [config_file=config.yml]" if argv[0] == "--help"
84
+
85
+ config = YAML.load_file(argv[0] || "config.yml")
86
+
87
+ nv = Niconico.new(config["account"]["mail"], config["account"]["password"])
88
+
89
+ ffmpeg = config["ffmpeg"] || "ffmpeg"
90
+
91
+ cache_dir = File.expand_path(config["directories"]["cache"])
92
+ output_dir = File.expand_path(config["directories"]["output"])
93
+ tmpdir = File.expand_path(config["directories"]["temporary"])
94
+
95
+ if File.exist?(tmpdir)
96
+ FileUtils.remove_entry_secure(tmpdir)
97
+ end
98
+ FileUtils.mkdir(tmpdir)
99
+
100
+ converted = []
101
+ if File.exist?(output_dir)
102
+ nicoloid_converted = "#{output_dir}/nicoloid_converted"
103
+
104
+ if File.exist?(nicoloid_converted)
105
+ converted = open(nicoloid_converted,&:readlines).map(&:chomp)
106
+ end
107
+ else
108
+ FileUtils.mkdir output_dir
109
+ end
110
+
111
+ nv.agent.set_proxy(config["proxy"]["host"],config["proxy"]["port"]) if config["proxy"]
112
+
113
+ max = config["limit"] ? config["limit"].to_i : 10
114
+
115
+ puts "<bold><blue>=&gt;</blue> <white>Loading list...</white></bold>".termcolor
116
+
117
+ videos = []
118
+
119
+ case config["source"]["from"]
120
+ when nil, "ranking"
121
+ config["source"]["category"] ||= config["source"]["category"].to_sym \
122
+ || :vocaloid
123
+ %w(method span category).each do |k|
124
+ config["source"][k.to_sym] = config["source"][k].to_sym if config["source"][k]
125
+ end
126
+
127
+ videos = nv.ranking(config["source"]["category"], config["source"])
128
+ when "vocaran"
129
+ latest_vocaran = nv.mylist(9352163)[-1]
130
+ if /(PL|PL) ?[::] ?mylist\/(\d+)/ =~ latest_vocaran.description
131
+ mylist_id = $2
132
+ videos = nv.mylist(mylist_id)
133
+ sleep 1
134
+ else
135
+ warn "<bold><red>=&gt;</red> <white>Failed to detect mylist</white></bold>".termcolor
136
+ end
137
+ else
138
+ warn "WARNING: source name is wrong or not supported. you gave #{config["source"]["from"]} as source name."
139
+ end
140
+
141
+ videos[0...max].each_with_index do |v, i|
142
+ puts "<bold><blue>=&gt;</blue> <white>#{i+1}. (#{v.id}) #{v.title}</white><bold>".termcolor
143
+ begin
144
+ skip_message = "<bold><green> *</green> <white>Skipped</white></bold>".termcolor
145
+ (puts skip_message.termcolor; next) if converted.include?(v.id)
146
+ (puts skip_message; sleep 7; next) if v.type == :swf
147
+
148
+ videoname = "#{cache_dir}/#{v.id}.#{v.type}"
149
+ thumbname = "#{tmpdir}/#{v.id}.jpg"
150
+ cookie_jar = "#{tmpdir}/cookie.txt"
151
+
152
+ video_downloaded = false
153
+ puts_progress "Downloading video" do
154
+ unless Dir["#{cache_dir}/#{v.id}.*"].empty?
155
+ puts "<green> *</green> <white>Using from cache</white>".termcolor
156
+ break
157
+ end
158
+ puts "<bold><red> *</red> <white>Seems economy</white></bold>".termcolor if v.economy?
159
+ if (`curl --help` rescue nil)
160
+ a = v.get_video_by_other
161
+ cookies = a[:cookie]
162
+ url = a[:url]
163
+
164
+ open(cookie_jar, "w") do |io|
165
+ io.puts cookies.map { |cookie|
166
+ [cookie.domain, "TRUE", cookie.path,
167
+ cookie.secure.inspect.upcase, cookie.expires.to_i,
168
+ cookie.name, cookie.value].join("\t")
169
+ }.join("\n")
170
+ end
171
+
172
+ unless system("curl", "-#", "-o", videoname, "-b", cookie_jar, url)
173
+ puts "<bold><red>=&gt;</red> <white>Failed...</white></bold>".termcolor
174
+ end
175
+ else
176
+ puts "<bold><red> *</red> <white>if you have a curl, you can download videos with progress bar.</white></bold>".termcolor
177
+ open(videoname,"wb") do |f|
178
+ f.print v.get_video
179
+ end
180
+ end
181
+ end
182
+ video_downloaded = true
183
+
184
+ filename = nil
185
+ puts_progress "Converting to mp3" do
186
+ #outext = `ffmpeg -i #{videoname} 2>&1`[/^ +Stream.+?: Audio: (.+?),/, 1]
187
+ outext = nil
188
+ basename = "#{i+1}_#{v.id}_#{v.title.gsub(/\//,"-")}.#{outext||"mp3"}"
189
+ filename = "#{output_dir}/#{basename}"
190
+
191
+ cmd = if outext
192
+ [ffmpeg, "-loglevel", "quiet", "-i", videoname, "-acodec", "copy", filename]
193
+ else
194
+ [ffmpeg, "-loglevel", "quiet", "-i", videoname, "-ab", "320k", filename]
195
+ end
196
+ unless system(*cmd)
197
+ puts "<bold><red>=&gt;</red> <white>Failed...</white></bold>".termcolor
198
+ exit 1
199
+ end
200
+ end
201
+
202
+ artists=artist_sorting=nil
203
+ puts_progress "Detecting artists" do
204
+ vt = v.title
205
+
206
+ artists = v.tags.select{|tag| VOCALOIDS.include?(tag) }
207
+
208
+ if artists.empty?
209
+ puts "<green>=&gt;</green> <white>Detection by tags failed. try detecting from the title...</white>".termcolor
210
+ artists = VOCALOIDS.inject([]){|r,i| vt.include?(i) ? r << i : r }
211
+ end
212
+
213
+ artists.map!{|i| VOCALOIDS_ALIAS.key?(i) ? VOCALOIDS_ALIAS[i] : i }
214
+ artists.uniq!
215
+
216
+ artist_sorting = artists.map{|i| VOCALOIDS_SORTING[i] || i}.join(', ')
217
+ artists = artists.join(', ')
218
+
219
+ puts
220
+ puts " #{artists}"
221
+ puts " #{artist_sorting}"
222
+ puts
223
+ end
224
+
225
+ puts_progress "Exporting thumbnail" do
226
+ unless system(ffmpeg, "-loglevel", "quiet", "-i", videoname, *%w(-f image2 -vframes 1 -ss 10 -an -deinterlace), thumbname)
227
+ puts "<bold><red>=&gt;</red> <white>Failed...</white></bold>".termcolor
228
+ exit 1
229
+ end
230
+ end
231
+
232
+ puts_progress "Setting ID3 Tags..." do
233
+ file = TagLib::MPEG::File.new(filename)
234
+ tag = file.id3v2_tag
235
+ tag.artist = artists
236
+ tag.title = v.title
237
+ tag.album = v.title
238
+
239
+ cover = TagLib::ID3v2::AttachedPictureFrame.new
240
+ cover.mime_type = "image/jpeg"
241
+ cover.description = "cover"
242
+ cover.type = TagLib::ID3v2::AttachedPictureFrame::FrontCover
243
+ cover.picture = File.open(thumbname, "rb").read
244
+
245
+ sort = TagLib::ID3v2::TextIdentificationFrame.new("TSOP",TagLib::String::UTF8)
246
+ sort.text = artist_sorting
247
+
248
+ tag.add_frame(cover)
249
+ tag.add_frame(sort)
250
+
251
+ file.save
252
+ end
253
+ rescue Exception => e
254
+ files = [filename,tmpdir]
255
+ files << videoname unless video_downloaded
256
+ files.each do |f|
257
+ FileUtils.remove_entry_secure f if File.exist?(f)
258
+ end
259
+ if e.class == Interrupt
260
+ puts
261
+ puts_progress("Interrupt."){}
262
+ exit
263
+ else
264
+ raise e
265
+ end
266
+ else
267
+ converted << v.id
268
+ open(nicoloid_converted,"w") do |f|
269
+ f.print converted.join("\n")
270
+ end
271
+ end
272
+
273
+ puts_progress "Wait..." do
274
+ 5.times {print "."; sleep 1}
275
+ puts
276
+ puts
277
+ end
278
+ end
279
+
280
+ puts_progress "Removing temporary directory" do
281
+ FileUtils.remove_entry_secure tmpdir
282
+ end
283
+ end
284
+ end
285
+ end
286
+
@@ -0,0 +1,3 @@
1
+ module Nicoloid
2
+ VERSION = "1.0.0"
3
+ end
data/nicoloid.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "nicoloid/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "nicoloid"
7
+ s.version = Nicoloid::VERSION
8
+ s.authors = ["Shota Fukumori (sora_h)"]
9
+ s.email = ["sorah@tubusu.net"]
10
+ s.homepage = ""
11
+ s.summary = "Download video from nicovideo and convert to mp3"
12
+ s.description = "nicoloid downloads video from nicovideo.jp, and convert videos to mp3 using ffmpeg."
13
+
14
+ s.rubyforge_project = "nicoloid"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nicoloid
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Shota Fukumori (sora_h)
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-17 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: nicoloid downloads video from nicovideo.jp, and convert videos to mp3
15
+ using ffmpeg.
16
+ email:
17
+ - sorah@tubusu.net
18
+ executables:
19
+ - nicoloid
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - .gitignore
24
+ - Gemfile
25
+ - README.mkd
26
+ - Rakefile
27
+ - bin/nicoloid
28
+ - lib/nicoloid.rb
29
+ - lib/nicoloid/version.rb
30
+ - nicoloid.gemspec
31
+ homepage: ''
32
+ licenses: []
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project: nicoloid
51
+ rubygems_version: 1.8.11
52
+ signing_key:
53
+ specification_version: 3
54
+ summary: Download video from nicovideo and convert to mp3
55
+ test_files: []
56
+ has_rdoc: