nv 1.0.1 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5f8b27af310126a64b577f8bc0720f7b26b2dd06
4
- data.tar.gz: 7cd30e5c8111192be2c391e2051c234061dec6f3
3
+ metadata.gz: c5f0c8bd1b296caf18b4fcce67f896e3349b2ff4
4
+ data.tar.gz: 2cf890bcaf3da97043709f15940ffdc986e53817
5
5
  SHA512:
6
- metadata.gz: 0a7051e1beefe24355ef142acdb43557810cc88e561d9268c0b030556f1aa7aef219068b1e41428aa5ffa1d2e2370ff211ac51633adaf4a8d44e74fd5d7b15e5
7
- data.tar.gz: eb829204f102156b78306425e3cff1d4a71de1b319438a38d1dc61aa069dd80c462823237e451cb38e6f2a057d2496d4ba106d54383e3f6a971311c2f491f023
6
+ metadata.gz: c9769a36ba372b6457831268ca13e2145db62b5fd502744d0012d7c7d158e0fc284745ec87bffc95bd9854bcb3ef84335b8c26eecda51e081ba7ecc12e349cb6
7
+ data.tar.gz: dc112b9d3c8909c694c732e6c9e17d077557be2090386414c460e84f1485f21b42cc84bf5c3d175172e1a58058952644c53c5d7574ef9040b8672c06d865d92b
data/README.md CHANGED
@@ -17,7 +17,7 @@ $ nv config email john@example.com
17
17
  $ nv config password pAsSwoRd
18
18
  ```
19
19
 
20
- Default config file will be put on __$HOME/.config/nv__
20
+ Default config file will be put on __~/.config/nv__
21
21
 
22
22
  ### Download
23
23
 
@@ -26,13 +26,28 @@ $ nv dl http://www.nicovideo.jp/watch/sm22538737
26
26
  $ nv dl http://www.nicovideo.jp/mylist/33435425
27
27
  ```
28
28
 
29
- Also you can use more easy way.
29
+ You also can use more shorten addresses.
30
30
 
31
31
  ```session
32
32
  $ nv dl sm9
33
33
  $ nv dl mylist/33435425
34
34
  ```
35
35
 
36
+ #### Options
37
+
38
+ ##### Directory
39
+
40
+ ```session
41
+ $ nv dl sm9 --with-dir
42
+ $ nv dl mylist/33435425 --without-dir
43
+ ```
44
+
45
+ ##### Comments
46
+
47
+ ```session
48
+ $ nv dl sm9 --with-comments
49
+ ```
50
+
36
51
  ### Audit
37
52
 
38
53
  ```session
data/bin/nv CHANGED
@@ -3,4 +3,9 @@
3
3
 
4
4
  require 'nv'
5
5
 
6
- Nv::CLI.start(ARGV)
6
+ begin
7
+ Nv::CLI.start(ARGV)
8
+ rescue SystemExit, Interrupt
9
+ rescue Exception => e
10
+ puts e
11
+ end
data/lib/nv.rb CHANGED
@@ -5,18 +5,6 @@ require "nv/cli"
5
5
 
6
6
  =begin
7
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
8
  ##### Way #####
21
9
 
22
10
  1. nico = Niconico::Base.new.sign_in(...)
@@ -33,5 +21,5 @@ end
33
21
  module Nv
34
22
  class LackOfInformation < StandardError; end
35
23
 
36
- CONFIG_PATH = File.join(ENV['HOME'], '.config', 'nv')
24
+ CONFIG_PATH = "#{ENV['HOME']}/.config/nv"
37
25
  end
@@ -3,7 +3,10 @@ require 'thor'
3
3
  class Nv::CLI < Thor
4
4
  include Niconico::Helper
5
5
 
6
- desc "dl URL", "Download video"
6
+ desc 'dl URL', 'Download video'
7
+ method_option 'with-comments', :aliases => '-c', :desc => 'Download comments'
8
+ method_option 'with-dir', :aliases => '-d', :desc => 'Create directory'
9
+ method_option 'without-dir', :aliases => '-D', :desc => "Don't create directory"
7
10
  def dl(ptr, output=".")
8
11
  config = Nv::Config.new(Nv::CONFIG_PATH)
9
12
  config.verify_for_authentication!('dl')
@@ -14,7 +17,10 @@ class Nv::CLI < Thor
14
17
  mylist = nico.mylist(ptr)
15
18
 
16
19
  puts "Title : #{mylist.title}"
17
- puts "Desc : #{mylist.description}"
20
+ puts "Desc : #{mylist.description}" unless mylist.description.empty?
21
+
22
+ escaped_title = escape_string(mylist.title)
23
+ output = options['without-dir'] ? '.' : escaped_title
18
24
 
19
25
  mylist.items.each do |item|
20
26
  dl(item.link, output)
@@ -25,14 +31,17 @@ class Nv::CLI < Thor
25
31
  # Inspect
26
32
  puts "Downloading... #{video.title}"
27
33
 
28
- # Donwload
29
- video.download
34
+ output ||= options['with-dir'] ? escape_string(video.title) : '.'
35
+
36
+ # Download
37
+ video.download output
38
+ video.download_comments output if options['with-comments']
30
39
 
31
40
  puts "+ done"
32
41
  end
33
42
  end
34
43
 
35
- desc "info URL", "Show video/mylist info"
44
+ desc 'info URL', 'Show video/mylist info'
36
45
  def info(ptr)
37
46
  config = Nv::Config.new(Nv::CONFIG_PATH)
38
47
  config.verify_for_authentication!('info')
@@ -52,21 +61,27 @@ class Nv::CLI < Thor
52
61
  video = nico.video(ptr)
53
62
 
54
63
  puts video.title
55
- puts "=" * 40
64
+ puts '=' * 40
56
65
  puts video.description
57
- puts "=" * 40
66
+ puts '=' * 40
58
67
  puts "URL: #{video.watch_url}"
59
68
  end
60
69
  end
61
70
 
62
- desc "config KEY VALUE", "Set config"
71
+ desc 'browse FILE', 'Open web-browser to show nicovideo page with given movie file'
72
+ def browse(filepath)
73
+ video_id = File.basename(filepath).match(/[^\w]([\w]{2}\d+)[^\w]/)[1]
74
+ system "open http://www.nicovideo.jp/watch/#{video_id}"
75
+ end
76
+
77
+ desc 'config KEY VALUE', 'Set config'
63
78
  def config(key=nil, value=nil)
64
79
  config = Nv::Config.new(Nv::CONFIG_PATH)
65
80
 
66
81
  unless key
67
- puts "config:"
82
+ puts "=== config(#{Nv::CONFIG_PATH}) ==="
68
83
  config.to_h.each do |k, v|
69
- puts " #{k}=#{v}"
84
+ puts "#{k}=#{v}"
70
85
  end
71
86
  return
72
87
  end
@@ -76,6 +91,12 @@ class Nv::CLI < Thor
76
91
  config.save
77
92
  end
78
93
 
79
- puts "config: #{key}=#{config[key]}"
94
+ puts "=== config(#{Nv::CONFIG_PATH}) ==="
95
+ puts "#{key}=#{config[key]}"
96
+ end
97
+
98
+ desc "version", "Show version"
99
+ def version
100
+ puts "nv version #{Nv::VERSION}"
80
101
  end
81
102
  end
@@ -1,7 +1,6 @@
1
1
  require 'ostruct'
2
2
  require 'yaml'
3
3
  require 'fileutils'
4
- require 'active_support/core_ext'
5
4
 
6
5
  class String
7
6
  def undent
@@ -25,7 +24,7 @@ module Nv
25
24
 
26
25
  def save
27
26
  File.open(@config_path, 'w') do |f|
28
- f.print YAML.dump(self.to_h.stringify_keys)
27
+ f.print YAML.dump(transform_keys(self.to_h){|k| k.to_s})
29
28
  end
30
29
  end
31
30
 
@@ -43,5 +42,15 @@ module Nv
43
42
  exit
44
43
  end
45
44
  end
45
+
46
+ private
47
+
48
+ def transform_keys(hs)
49
+ result = {}
50
+ hs.each_key do |key|
51
+ result[yield(key)] = hs[key]
52
+ end
53
+ result
54
+ end
46
55
  end
47
56
  end
@@ -1,5 +1,6 @@
1
1
  require 'mechanize'
2
2
  require 'open-uri'
3
+ require 'net/http'
3
4
  require 'uri'
4
5
  require 'cgi'
5
6
  require 'rexml/document'
@@ -7,14 +8,12 @@ require 'rss'
7
8
  require 'ostruct'
8
9
  require 'ruby-progressbar'
9
10
 
10
- $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
11
-
12
- require 'niconico/helper'
13
- require 'niconico/fabric'
14
- require 'niconico/base'
15
- require 'niconico/video'
16
- require 'niconico/mylist'
11
+ require 'nv/niconico/helper'
12
+ require 'nv/niconico/fabric'
13
+ require 'nv/niconico/base'
14
+ require 'nv/niconico/video'
15
+ require 'nv/niconico/mylist'
17
16
 
18
17
  module Niconico
19
-
18
+ OUTPUT_NAME = "%{title} - [%{id}].%{extension}"
20
19
  end
@@ -4,5 +4,9 @@ module Niconico
4
4
  return true if url =~ /mylist\/\d+/
5
5
  false
6
6
  end
7
+
8
+ def escape_string(str)
9
+ str.gsub(/[\/\\?*:|><]/) {|m| [m.ord + 65248].pack('U*')}
10
+ end
7
11
  end
8
12
  end
@@ -22,7 +22,7 @@ module Niconico
22
22
  end
23
23
 
24
24
  def fetch
25
- doc = REXML::Document.new(@agent.get("http://www.nicovideo.jp/mylist/#{@id}?rss=2.0").body)
25
+ doc = REXML::Document.new(open("http://www.nicovideo.jp/mylist/#{@id}?rss=2.0").read)
26
26
 
27
27
  channel = doc.elements['/rss/channel']
28
28
 
@@ -42,14 +42,14 @@ module Niconico
42
42
  end
43
43
 
44
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()'],
45
+ :title => channel.elements['title/text()'].to_s.gsub(/(^マイリスト\s|‐ニコニコ動画$)/, ''),
46
+ :link => channel.elements['link/text()'].to_s,
47
+ :description => channel.elements['description/text()'].to_s,
48
+ :created_at => channel.elements['pubDate/text()'].to_s,
49
+ :updated_at => channel.elements['lastBuildDate/text()'].to_s,
50
+ :generator => channel.elements['generator/text()'].to_s,
51
+ :author => channel.elements['dc:creator/text()'].to_s,
52
+ :language => channel.elements['language/text()'].to_s,
53
53
  :items => items,
54
54
  :items_count => items.size
55
55
  })
@@ -11,32 +11,118 @@ module Niconico
11
11
  end
12
12
 
13
13
  def download(output=".")
14
- escapedTitle = thumb.title.gsub(/\//, "/")
15
- filename = "#{escapedTitle} - [#{thumb.video_id}].#{thumb.extension}"
16
- filepath = File.join(output, filename)
14
+ escaped_title = escape_string(thumb.title)
15
+ escaped_output = escape_string(output)
17
16
 
18
- Dir.mkdir(output) unless Dir.exist? output
17
+ filename = sprintf OUTPUT_NAME, {:title => escaped_title, :id => thumb.video_id, :extension => thumb.extension}
18
+ filepath = File.join(escaped_output, filename)
19
+ filepath_nvdownload = "#{filepath}.nvdownload"
19
20
 
21
+ # Return when video file is already exist
22
+ if File.exist? filepath
23
+ File.delete filepath_nvdownload if File.exist? filepath_nvdownload
24
+ return
25
+ end
26
+
27
+ # Create output dir
28
+ Dir.mkdir escaped_output unless Dir.exist? escaped_output
29
+
30
+ # Define request headers
31
+ options = {
32
+ 'Cookie' => flv.history_cookies
33
+ }
34
+ if File.exist? filepath_nvdownload
35
+ options['Range'] = "bytes=#{File.size(filepath_nvdownload)}-"
36
+ end
37
+
38
+ # Download video
20
39
  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"
40
+ url = URI.parse(flv.url)
41
+ begin
42
+ Net::HTTP.start(url.host, url.port) do |http|
43
+ header = http.request_head("#{url.path}?#{url.query}", options)
44
+ progress_bar = ProgressBar.create(:total => header['content-length'].to_i)
45
+ transferred_bytes = 0
46
+ request = Net::HTTP::Get.new(url, options)
47
+ http.request request do |response|
48
+ open(filepath_nvdownload, 'ab') do |io|
49
+ response.read_body do |chunk|
50
+ transferred_bytes += chunk.size
51
+ if progress_bar
52
+ progress_bar.progress = transferred_bytes
53
+ else
54
+ puts "#{transferred_bytes} / Total size is unknown"
55
+ end
56
+
57
+ io.write chunk
58
+ end
34
59
  end
35
- }
36
- ) do |f|
37
- fp.print f.read
60
+ end
38
61
  end
62
+ rescue => e
63
+ puts "Failed download: #{e}"
64
+ return
65
+ end
66
+
67
+ # Rename .nvdownload to real file
68
+ File.rename(filepath_nvdownload, filepath)
69
+ end
70
+
71
+ # GET http://flapi.nicovideo.jp/api/getwaybackkey?thread=1345476375
72
+ # => waybackkey=1417346808.E9d0LUF9gvFvt3Rrf5TP91Pa0LA
73
+
74
+ # POST http://msg.nicovideo.jp/53/api/
75
+ # <packet><thread thread="1345476375" version="20090904" user_id="1501297" scores="1" nicoru="1" with_global="1"/><thread_leaves thread="1345476375" user_id="1501297" scores="1" nicoru="1">0-14:100,1000</thread_leaves></packet>
76
+ # <packet>
77
+ # <thread thread="1345476375"
78
+ # version="20090904"
79
+ # waybackkey="1417346808.E9d0LUF9gvFvt3Rrf5TP91Pa0LA"
80
+ # when="1417346804"
81
+ # user_id="1501297"
82
+ # scores="1"
83
+ # nicoru="1"
84
+ # />
85
+ # <thread_leaves
86
+ # thread="1345476375"
87
+ # waybackkey="1417346808.E9d0LUF9gvFvt3Rrf5TP91Pa0LA"
88
+ # when="1417346804" # 起点
89
+ # user_id="1501297"
90
+ # res_before="8541"
91
+ # scores="1"
92
+ # nicoru="1" >0-14:100,1000</thread_leaves>
93
+ # </packet>
94
+ # <chat thread="1345476375" no="8540" vpos="55658" date="1417346426" mail="184" user_id="tzaiW5hp-SvJG6UGkEa0kELQd3w" anonymity="1" leaf="9">白いレースのハンカチかな?</chat>
95
+ # <chat thread="1345476375" no="8539" vpos="41534" date="1417233687" mail="184" user_id="1p_4U9fr-YvliRaUl3E6XGEavp4" premium="1" anonymity="1" leaf="6">くっそwwww</chat>
96
+ def download_comments(output=".")
97
+ escaped_title = escape_string(thumb.title)
98
+ escaped_output = escape_string(output)
99
+
100
+ filename = sprintf OUTPUT_NAME, {:title => escaped_title, :id => thumb.video_id, :extension => 'comments'}
101
+ filepath = File.join(escaped_output, filename)
102
+
103
+ Dir.mkdir(escaped_output) unless Dir.exist? escaped_output
104
+
105
+ url = URI.parse(flv.ms)
106
+
107
+ thread_id = flv.thread_id
108
+ length = (flv.l.to_i / 60).round
109
+ res = Net::HTTP.new(url.host).start do |http|
110
+ req = Net::HTTP::Post.new(url.path, {'Cookie' => flv.history_cookies})
111
+ req.body = %|<packet><thread thread="#{thread_id}" version="20090904" scores="1" nicoru="1" with_global="1"/><thread_leaves thread="#{thread_id}" scores="1" nicoru="1">0-#{length}:10</thread_leaves></packet>|
112
+ http.request(req)
39
113
  end
114
+
115
+ open(filepath, 'w') do |f|
116
+ f.write res.body
117
+ end
118
+
119
+ # TODO: Comment parser
120
+ # doc = REXML::Document.new(res.body)
121
+ # chats = doc.elements.to_a('/packet/chat')
122
+ # chats.each do |chat|
123
+ # puts chat.attribute('vpos')
124
+ # puts chat.text
125
+ # end
40
126
  end
41
127
 
42
128
  # Combined parameter fetcher
@@ -78,7 +164,6 @@ module Niconico
78
164
 
79
165
  def flv(id=@id)
80
166
  return @flv if @flv
81
-
82
167
  @agent.get("http://www.nicovideo.jp/watch/#{id}")
83
168
  history_cookie = @agent.cookies.map(&:to_s).join('; ')
84
169
 
@@ -1,3 +1,3 @@
1
1
  module Nv
2
- VERSION = "1.0.1"
2
+ VERSION = "1.2.1"
3
3
  end
data/nv.gemspec CHANGED
@@ -21,7 +21,6 @@ Gem::Specification.new do |spec|
21
21
  spec.add_development_dependency "bundler", "~> 1.7"
22
22
  spec.add_development_dependency "rake", "~> 10.0"
23
23
 
24
- spec.add_dependency "activesupport"
25
24
  spec.add_dependency "mechanize"
26
25
  spec.add_dependency "thor"
27
26
  spec.add_dependency "ruby-progressbar"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nv
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yasuaki Uechi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-30 00:00:00.000000000 Z
11
+ date: 2014-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,20 +38,6 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
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
41
  - !ruby/object:Gem::Dependency
56
42
  name: mechanize
57
43
  requirement: !ruby/object:Gem::Requirement