nv 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 34a85a4316b10f3de92c6280c366311795358446
4
+ data.tar.gz: f3ad31ade0fbb1988fa2975c3781c2a3a197db88
5
+ SHA512:
6
+ metadata.gz: 6d98bbcceea4386e9435c4dc184c1aa1ffafd90f91f824748d127d1e2d6d4a39b2584da2a551b95a66467dffa75f9059ba574c4fb9367d7e0285af24950d99af
7
+ data.tar.gz: 77dacada776466f7db59370ab071aad84eecb625e033dde988fccffcd459fedd991be0d18ae53e41a80bf43bf54031748637ab49bced5ec558f0a507f0663737
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ Gemfile.lock
2
+ /.bundle
3
+ /vendor/bundle
4
+ /pkg
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in nv.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Yasuaki Uechi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # nv
2
+
3
+ The commandline tool for downloading videos and mylist at nicovideo.
4
+
5
+ ## Installation
6
+
7
+ ```session
8
+ $ gem install nv
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Setup
14
+
15
+ ```session
16
+ $ nv config email john@example.com
17
+ $ nv config password pAsSwoRd
18
+ ```
19
+
20
+ ### Download
21
+
22
+ ```session
23
+ $ nv dl http://www.nicovideo.jp/watch/sm22538737
24
+ $ nv dl http://www.nicovideo.jp/mylist/33435425
25
+ ```
26
+
27
+ Also you can use more easy way.
28
+
29
+ ```session
30
+ $ nv dl sm9
31
+ $ nv dl mylist/33435425
32
+ ```
33
+
34
+ ### Audit
35
+
36
+ ```session
37
+ $ nv info sm9
38
+ $ nv info mylist/33435425
39
+ ```
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/nv ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require 'nv'
5
+
6
+ Nv::CLI.start(ARGV)
data/lib/nv.rb ADDED
@@ -0,0 +1,37 @@
1
+ require "nv/version"
2
+ require "nv/config"
3
+ require "nv/niconico"
4
+ require "nv/cli"
5
+
6
+ =begin
7
+
8
+ nico = Niconico.new.sign_in(...)
9
+ video = nico.video('sm9')
10
+ video.download('./')
11
+
12
+ mylist = nico.mylist('482029')
13
+ mylist.download()
14
+
15
+ include Niconico::Helper
16
+ if mylist? 'http://...'
17
+ ...
18
+ end
19
+
20
+ ##### Way #####
21
+
22
+ 1. nico = Niconico::Base.new.sign_in(...)
23
+ video = nico.video('sm9') => Niconico::Video
24
+ puts video.id
25
+ video.download
26
+
27
+ 2. video = Niconico::Video.new('sm9')
28
+ video.sign_in(...)
29
+ video.download
30
+
31
+ =end
32
+
33
+ module Nv
34
+ class LackOfInformation < StandardError; end
35
+
36
+ CONFIG_PATH = File.join(ENV['HOME'], '.config', 'nv')
37
+ end
data/lib/nv/cli.rb ADDED
@@ -0,0 +1,81 @@
1
+ require 'thor'
2
+
3
+ class Nv::CLI < Thor
4
+ include ::Niconico::Helper
5
+
6
+ desc "dl URL", "Download video"
7
+ def dl(ptr, output=".")
8
+ config = Nv::Config.new(Nv::CONFIG_PATH)
9
+ config.verify_for_authentication!('dl')
10
+
11
+ nico = Niconico::Base.new.sign_in(config.email, config.password)
12
+
13
+ if mylist?(ptr)
14
+ mylist = nico.mylist(ptr)
15
+
16
+ puts "Title : #{mylist.title}"
17
+ puts "Desc : #{mylist.description}"
18
+
19
+ mylist.items.each do |item|
20
+ dl(item.link, output)
21
+ end
22
+ else
23
+ video = nico.video(ptr)
24
+
25
+ # Inspect
26
+ puts "Downloading... #{video.title}"
27
+
28
+ # Donwload
29
+ video.download
30
+
31
+ puts "+ done"
32
+ end
33
+ end
34
+
35
+ desc "info URL", "Show video/mylist info"
36
+ def info(ptr)
37
+ config = Nv::Config.new(Nv::CONFIG_PATH)
38
+ config.verify_for_authentication!('info')
39
+
40
+ nico = Niconico::Base.new.sign_in(config.email, config.password)
41
+
42
+ if mylist?(ptr)
43
+ mylist = nico.mylist(ptr)
44
+
45
+ puts "Title : #{mylist.title}"
46
+ puts "Desc : #{mylist.description}"
47
+
48
+ mylist.items.each_with_index do |item, i|
49
+ puts " #{i+1}. #{item.title}"
50
+ end
51
+ else
52
+ video = nico.video(ptr)
53
+
54
+ puts video.title
55
+ puts "=" * 40
56
+ puts video.description
57
+ puts "=" * 40
58
+ puts "URL: #{video.watch_url}"
59
+ end
60
+ end
61
+
62
+ desc "config KEY VALUE", "Set config"
63
+ def config(key=nil, value=nil)
64
+ config = Nv::Config.new(Nv::CONFIG_PATH)
65
+
66
+ unless key
67
+ puts "config:"
68
+ config.to_h.each do |k, v|
69
+ puts " #{k}=#{v}"
70
+ end
71
+ return
72
+ end
73
+
74
+ if value
75
+ config[key] = value
76
+ config.save
77
+ end
78
+
79
+ puts "config: #{key}=#{config[key]}"
80
+ end
81
+ end
data/lib/nv/config.rb ADDED
@@ -0,0 +1,47 @@
1
+ require 'ostruct'
2
+ require 'yaml'
3
+ require 'fileutils'
4
+ require 'active_support/core_ext'
5
+
6
+ class String
7
+ def undent
8
+ gsub(/^.{#{slice(/^ +/).length}}/, '')
9
+ end
10
+ end
11
+
12
+ module Nv
13
+ class Config < OpenStruct
14
+ def initialize(config_path)
15
+ # Initialize config file
16
+ @config_path = config_path
17
+ config_dir = File.dirname(@config_path)
18
+
19
+ Dir.mkdir(config_dir) unless Dir.exist?(config_dir)
20
+ FileUtils.touch(@config_path) unless File.exist?(@config_path)
21
+
22
+ @config = YAML.load_file(@config_path) || {}
23
+ super(@config)
24
+ end
25
+
26
+ def save
27
+ File.open(@config_path, 'w') do |f|
28
+ f.print YAML.dump(self.to_h.stringify_keys)
29
+ end
30
+ end
31
+
32
+ def verify_for_authentication
33
+ self.email && self.password
34
+ end
35
+
36
+ def verify_for_authentication!(cmd)
37
+ unless verify_for_authentication
38
+ puts <<-EOD.undent
39
+ `nv #{cmd}` should be given email and password.
40
+ $ nv config email <email>
41
+ $ nv config password <password>
42
+ EOD
43
+ exit
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,18 @@
1
+ require 'mechanize'
2
+ require 'open-uri'
3
+ require 'uri'
4
+ require 'cgi'
5
+ require 'rexml/document'
6
+ require 'rss'
7
+ require 'ostruct'
8
+ require 'ruby-progressbar'
9
+
10
+ require 'niconico/helper'
11
+ require 'niconico/fabric'
12
+ require 'niconico/base'
13
+ require 'niconico/video'
14
+ require 'niconico/mylist'
15
+
16
+ module Niconico
17
+
18
+ end
@@ -0,0 +1,11 @@
1
+ module Niconico
2
+ class Base < Fabric
3
+ def video(video_id)
4
+ Niconico::Video.new(video_id, @agent)
5
+ end
6
+
7
+ def mylist(mylist_id)
8
+ Niconico::Mylist.new(mylist_id, @agent)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ module Niconico
2
+ class Fabric
3
+ attr_reader :agent
4
+
5
+ def initialize(agent=nil)
6
+ @agent = agent || Mechanize.new
7
+ @agent.verify_mode = OpenSSL::SSL::VERIFY_NONE
8
+ end
9
+
10
+ def sign_in(email, password)
11
+ @agent.post(
12
+ "https://secure.nicovideo.jp/secure/login?site=niconico",
13
+ "mail" => email,
14
+ "password" => password
15
+ )
16
+ return self
17
+ end
18
+
19
+ def signed_in?
20
+ @agent.cookies.any? {|c| c.name == 'user_session'}
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,8 @@
1
+ module Niconico
2
+ module Helper
3
+ def mylist?(url)
4
+ return true if url =~ /mylist\/\d+/
5
+ false
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,58 @@
1
+ module Niconico
2
+ class Mylist < Fabric
3
+ include Niconico::Helper
4
+
5
+ def initialize(ptr, agent=nil)
6
+ super(agent)
7
+
8
+ @id = normalize(ptr)
9
+ @mylist = nil
10
+ fetch
11
+ end
12
+
13
+ def method_missing(method, *args)
14
+ raise(NoMethodError, method) unless @mylist.respond_to? method
15
+ @mylist[method]
16
+ end
17
+
18
+ private
19
+
20
+ def normalize(ptr)
21
+ ptr.match(/mylist\/([0-9]+)\??/)[1]
22
+ end
23
+
24
+ def fetch
25
+ doc = REXML::Document.new(@agent.get("http://www.nicovideo.jp/mylist/#{@id}?rss=2.0").body)
26
+
27
+ channel = doc.elements['/rss/channel']
28
+
29
+ items = []
30
+ channel.elements.each('item') do |item|
31
+ html_description = item.elements['description/text()'].to_s.gsub(/<p class=\"nico-info\">.+<\/p>/, '')
32
+ description = html_description.gsub(/<\/?.*?>/, '')
33
+
34
+ items << OpenStruct.new({
35
+ :title => item.elements['title/text()'].to_s,
36
+ :link => item.elements['link/text()'].to_s,
37
+ :guid => item.elements['guid/text()'].to_s,
38
+ :created_at => item.elements['pubDate/text()'].to_s,
39
+ :description => description,
40
+ :html_description => html_description
41
+ })
42
+ end
43
+
44
+ @mylist = OpenStruct.new({
45
+ :title => channel.elements['title/text()'],
46
+ :link => channel.elements['link/text()'],
47
+ :description => channel.elements['description/text()'],
48
+ :created_at => channel.elements['pubDate/text()'],
49
+ :updated_at => channel.elements['lastBuildDate/text()'],
50
+ :generator => channel.elements['generator/text()'],
51
+ :author => channel.elements['dc:creator/text()'],
52
+ :language => channel.elements['language/text()'],
53
+ :items => items,
54
+ :items_count => items.size
55
+ })
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,91 @@
1
+ module Niconico
2
+ class Video < Fabric
3
+ include Niconico::Helper
4
+
5
+ def initialize(ptr, agent=nil)
6
+ super(agent)
7
+
8
+ @thumb = nil
9
+ @flv = nil
10
+ @id = normalize(ptr) # pvid | thumb_cached
11
+ end
12
+
13
+ def download(output=".")
14
+ escapedTitle = thumb.title.gsub(/\//, "/")
15
+ filename = "#{escapedTitle} - [#{thumb.video_id}].#{thumb.extension}"
16
+ filepath = File.join(output, filename)
17
+
18
+ Dir.mkdir(output) unless Dir.exist? output
19
+
20
+ progress_bar = nil
21
+ File.open(filepath, 'wb') do |fp|
22
+ open(flv.url, 'rb',
23
+ 'Cookie' => flv.history_cookies,
24
+ :content_length_proc => lambda{ |content_length|
25
+ if content_length
26
+ progress_bar = ProgressBar.create(:total => content_length)
27
+ end
28
+ },
29
+ :progress_proc => lambda{ |transferred_bytes|
30
+ if progress_bar
31
+ progress_bar.progress = transferred_bytes
32
+ else
33
+ puts "#{transferred_bytes} / Total size is unknown"
34
+ end
35
+ }
36
+ ) do |f|
37
+ fp.print f.read
38
+ end
39
+ end
40
+ end
41
+
42
+ # Combined parameter fetcher
43
+ def method_missing(method, *args)
44
+ thumb[method] || flv[method] || raise(NoMethodError, method)
45
+ end
46
+
47
+ private
48
+
49
+ def strip_id(url)
50
+ url.match(/(?:watch\/)?(\w{2}?\d+)/)[1]
51
+ end
52
+
53
+ def normalize(ptr)
54
+ vid = strip_id(ptr)
55
+ thumb(vid).perm_video_id unless vid =~ /\A\d+\z/
56
+ end
57
+
58
+ def id
59
+ @id
60
+ end
61
+
62
+ def thumb(id=@id)
63
+ return @thumb if @thumb
64
+
65
+ doc = REXML::Document.new(open("http://ext.nicovideo.jp/api/getthumbinfo/#{id}"))
66
+ watch_url = doc.elements['nicovideo_thumb_response/thumb/watch_url'].text.to_s
67
+ perm_video_id = strip_id(watch_url)
68
+
69
+ @thumb = OpenStruct.new({
70
+ :video_id => doc.elements['nicovideo_thumb_response/thumb/video_id'].text.to_s,
71
+ :perm_video_id => perm_video_id,
72
+ :watch_url => watch_url,
73
+ :extension => doc.elements['nicovideo_thumb_response/thumb/movie_type'].text.to_s,
74
+ :title => doc.elements['nicovideo_thumb_response/thumb/title'].text.to_s,
75
+ :description => doc.elements['nicovideo_thumb_response/thumb/description'].text.to_s
76
+ })
77
+ end
78
+
79
+ def flv(id=@id)
80
+ return @flv if @flv
81
+
82
+ @agent.get("http://www.nicovideo.jp/watch/#{id}")
83
+ history_cookie = @agent.cookies.map(&:to_s).join('; ')
84
+
85
+ flv_str = @agent.get("http://www.nicovideo.jp/api/getflv?v=#{id}").body
86
+ flv_hash = Hash[flv_str.split("&").map{|e| i=e.split('=');i[1]=CGI.unescape(i[1]);i }]
87
+ flv_hash.update(:history_cookies => history_cookie)
88
+ @flv = OpenStruct.new(flv_hash)
89
+ end
90
+ end
91
+ end
data/lib/nv/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Nv
2
+ VERSION = "1.0.0"
3
+ end
data/nv.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'nv/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "nv"
8
+ spec.version = Nv::VERSION
9
+ spec.authors = ["Yasuaki Uechi"]
10
+ spec.email = ["uetchy@randompaper.co"]
11
+ spec.summary = %q{The toolbelt for nicovideo}
12
+ spec.description = %q{The commandline tool for downloading videos and mylist at nicovideo.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+
24
+ spec.add_dependency "activesupport"
25
+ spec.add_dependency "mechanize"
26
+ spec.add_dependency "thor"
27
+ spec.add_dependency "ruby-progressbar"
28
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nv
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Yasuaki Uechi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mechanize
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: thor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: ruby-progressbar
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: The commandline tool for downloading videos and mylist at nicovideo.
98
+ email:
99
+ - uetchy@randompaper.co
100
+ executables:
101
+ - nv
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - bin/nv
111
+ - lib/nv.rb
112
+ - lib/nv/cli.rb
113
+ - lib/nv/config.rb
114
+ - lib/nv/niconico.rb
115
+ - lib/nv/niconico/base.rb
116
+ - lib/nv/niconico/fabric.rb
117
+ - lib/nv/niconico/helper.rb
118
+ - lib/nv/niconico/mylist.rb
119
+ - lib/nv/niconico/video.rb
120
+ - lib/nv/version.rb
121
+ - nv.gemspec
122
+ homepage: ''
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.4.2
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: The toolbelt for nicovideo
146
+ test_files: []