vnehm 1.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.
@@ -0,0 +1,85 @@
1
+ module Vnehm
2
+ class HelpCommand < Command
3
+
4
+ SPACES_BTWN_NAME_AND_DESC = 3
5
+
6
+ def execute
7
+ command_name = options[:args].pop
8
+ if command_name.nil?
9
+ UI.say HELP
10
+ UI.term
11
+ end
12
+
13
+ @cmd = CommandManager.find_command(command_name)
14
+
15
+ show_usage
16
+ show_summary
17
+ show_arguments unless @cmd.arguments.empty?
18
+ show_options unless @cmd.options.empty?
19
+ end
20
+
21
+ def arguments
22
+ { 'КОМАНДА' => 'имя команды (может быть сокращено), справку которой нужно показать' }
23
+ end
24
+
25
+ def program_name
26
+ 'vnehm help'
27
+ end
28
+
29
+ def summary
30
+ 'Показ справки для определенной команды'
31
+ end
32
+
33
+ def usage
34
+ "#{program_name} КОМАНДА"
35
+ end
36
+
37
+ private
38
+
39
+ def find_longest_name(names)
40
+ names.inject do |longest, word|
41
+ word.length > longest.length ? word : longest
42
+ end
43
+ end
44
+
45
+ def show_usage
46
+ UI.say "#{'Использование:'.yellow} #{@cmd.usage}"
47
+ end
48
+
49
+ def show_summary
50
+ UI.newline
51
+ UI.say "#{'Описание:'.yellow}"
52
+ UI.say " #{@cmd.summary}"
53
+ end
54
+
55
+ def show_arguments
56
+ UI.newline
57
+ UI.say "#{'Аргументы:'.yellow}"
58
+ show_info(@cmd.arguments)
59
+ end
60
+
61
+ def show_options
62
+ UI.newline
63
+ UI.say "#{'Опции:'.yellow}"
64
+ show_info(@cmd.options_descs)
65
+ end
66
+
67
+ def show_info(hash)
68
+ @longest ||= nil
69
+
70
+ unless @longest
71
+ names = []
72
+ names += @cmd.arguments.keys unless @cmd.arguments.empty?
73
+ names += @cmd.options_descs.keys unless @cmd.options_descs.empty?
74
+ @longest ||= find_longest_name(names).length
75
+ end
76
+
77
+ hash.each do |name, desc|
78
+ need_spaces = @longest - name.length
79
+
80
+ UI.say " #{name.green}#{' ' * (need_spaces + SPACES_BTWN_NAME_AND_DESC)}#{desc}"
81
+ end
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,50 @@
1
+ require 'vnehm/tracks_view_command'
2
+
3
+ module Vnehm
4
+
5
+ ##
6
+ # This command gets likes/posts from user's account,
7
+ # Prints as menu, and downloads selected tracks
8
+
9
+ class ListCommand < TracksViewCommand
10
+
11
+ def initialize
12
+ super
13
+
14
+ add_option(:to, 'to ПУТЬ',
15
+ 'Скачивать аудиозапись(и) в ПУТЬ')
16
+
17
+ add_option(:pl, 'pl ПЛЕЙЛИСТ',
18
+ "Добавлять аудиозапись(и) в плейлист iTunes'a c именем" \
19
+ ' ПЛЕЙЛИСТ')
20
+
21
+ add_option(:limit, 'limit ЧИСЛО',
22
+ 'Показывать ЧИСЛО аудиозаписей на каждой страницe')
23
+
24
+ add_option(:offset, 'offset ЧИСЛО',
25
+ 'Показывать с ЧИСЛО+1 аудиозаписи')
26
+
27
+ add_option(:dl, 'dl yes',
28
+ 'Не добавлять аудиозапись(и) в iTunes. Просто скачать их')
29
+ end
30
+
31
+ def program_name
32
+ 'vnehm list'
33
+ end
34
+
35
+ def summary
36
+ 'Вывод списка ваших аудиозаписей из VK и загрузка выбранных треков'
37
+ end
38
+
39
+ def usage
40
+ "#{program_name} [ОПЦИИ]"
41
+ end
42
+
43
+ protected
44
+
45
+ def get_tracks
46
+ @track_manager.tracks(@limit, @offset)
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,67 @@
1
+ require 'vnehm/tracks_view_command'
2
+
3
+ module Vnehm
4
+
5
+ class SearchCommand < TracksViewCommand
6
+
7
+ def initialize
8
+ super
9
+
10
+ add_option(:"-t", '-t ПУТЬ',
11
+ 'Скачивать аудиозапись(и) в ПУТЬ')
12
+
13
+ add_option(:"-pl", '-pl ПЛЕЙЛИСТ',
14
+ "Добавлять аудиозапись(и) в плейлист iTunes'a c именем" \
15
+ ' ПЛЕЙЛИСТ')
16
+
17
+ add_option(:"-lim", '-lim ЧИСЛО',
18
+ 'Показывать ЧИСЛО аудиозаписей на каждой страницe')
19
+
20
+ add_option(:"-dl", '-dl yes',
21
+ 'Не добавлять аудиозаписи в iTunes. Просто скачать их')
22
+
23
+ end
24
+
25
+ def execute
26
+ # Convert dash-options to normal options
27
+ options_to_convert = { :"-t" => :to,
28
+ :"-pl" => :pl,
29
+ :"-lim" => :limit,
30
+ :"-dl" => :dl }
31
+
32
+ options_to_convert.each do |k,v|
33
+ value = @options[k]
34
+ @options.delete(k)
35
+ @options[v] = value unless value.nil?
36
+ end
37
+
38
+ @query = @options[:args].join(' ')
39
+ super
40
+ end
41
+
42
+ def arguments
43
+ { 'ЗАПРОС' => 'Искать аудиозаписи по ЗАПРОСу' }
44
+ end
45
+
46
+ def program_name
47
+ 'vnehm search'
48
+ end
49
+
50
+ def summary
51
+ 'Поиск, вывод и загрузка определенных аудиозаписей по запросу'
52
+ end
53
+
54
+ def usage
55
+ "#{program_name} ЗАПРОС [ОПЦИИ]"
56
+ end
57
+
58
+ protected
59
+
60
+ def get_tracks
61
+ UI.term 'Вы должны ввести запрос' if @query.empty?
62
+
63
+ @track_manager.search(@query, @limit, @offset)
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,23 @@
1
+ require 'vnehm/version'
2
+
3
+ module Vnehm
4
+ class VersionCommand < Command
5
+
6
+ def execute
7
+ UI.say VERSION
8
+ end
9
+
10
+ def program_name
11
+ 'vnehm version'
12
+ end
13
+
14
+ def summary
15
+ 'Вывод версии приложения'
16
+ end
17
+
18
+ def usage
19
+ program_name
20
+ end
21
+
22
+ end
23
+ end
data/lib/vnehm/menu.rb ADDED
@@ -0,0 +1,78 @@
1
+ module Vnehm
2
+ module UI
3
+ class Menu
4
+
5
+ def initialize
6
+ @choices = {}
7
+ @inc_index = 1
8
+ @items = []
9
+
10
+ yield self
11
+ select
12
+ end
13
+
14
+ def choice(index, desc, &block)
15
+ # Visual index - index that you see in menu
16
+ # Select index - index than can be selected
17
+ # For example, if you use ':added' index
18
+ # In menu you see 'A', but you can select it by number
19
+ # You receive a warning though
20
+
21
+ visual_index = select_index = index
22
+
23
+ if index == :inc
24
+ visual_index = select_index = @inc_index.to_s
25
+ @inc_index += 1
26
+ end
27
+
28
+ if index == :added
29
+ visual_index = 'A'.green
30
+ select_index = @inc_index.to_s
31
+ @inc_index += 1
32
+ end
33
+
34
+ @choices[select_index] = block
35
+ @items << "#{visual_index} #{desc}"
36
+ end
37
+
38
+ def header=(value)
39
+ @items.unshift(value)
40
+ end
41
+
42
+ def newline
43
+ @items << "\n"
44
+ end
45
+
46
+ def select
47
+ # Add exit option
48
+ newline
49
+ choice('e', 'Выйти'.red) { UI.term }
50
+
51
+ # Output items
52
+ @items.each do |item|
53
+ UI.say item
54
+ end
55
+
56
+ UI.newline
57
+
58
+ selected = UI.ask('Введите опцию:'.yellow.freeze)
59
+ call_selected_block(selected)
60
+
61
+ UI.newline
62
+ end
63
+
64
+ def call_selected_block(selected)
65
+ loop do
66
+ if @choices.keys.include? selected
67
+ block = @choices[selected]
68
+ block.call unless block.nil?
69
+ break
70
+ else
71
+ selected = UI.ask "Вы должны выбрать одно из [#{@choices.keys.join(', ')}]"
72
+ end
73
+ end
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,29 @@
1
+ module Vnehm
2
+
3
+ ##
4
+ # OptionParser parses options and add hash with options to specified command
5
+
6
+ class OptionParser
7
+
8
+ def initialize(args, command)
9
+ @args = args
10
+ @command = command
11
+ end
12
+
13
+ def parse
14
+ options = @command.options.keys.map(&:to_s)
15
+ options.each do |option|
16
+ if @args.include? option
17
+ index = @args.index(option)
18
+ value = @args[index + 1]
19
+ @args.delete_at(index + 1)
20
+ @args.delete_at(index)
21
+
22
+ @command.options[option.to_sym] = value
23
+ end
24
+ end
25
+ @command.options[:args] = @args
26
+ end
27
+
28
+ end
29
+ end
data/lib/vnehm/os.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'rbconfig'
2
+
3
+ module Vnehm
4
+
5
+ ##
6
+ # OS module returns information about OS on this computer
7
+
8
+ module OS
9
+ def self.mac?
10
+ RbConfig::CONFIG['host_os'] =~ /darwin|mac os/ ? true : false
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,49 @@
1
+ module Vnehm
2
+
3
+ ##
4
+ # Path manager works with download paths
5
+
6
+ module PathManager
7
+
8
+ def self.default_dl_path
9
+ Cfg[:dl_path]
10
+ end
11
+
12
+ ##
13
+ # Checks path for validation and returns it if valid
14
+
15
+ def self.get_path(path)
16
+ # Check path for existence
17
+ UI.term 'Такой папки не существует! Пожалуйста, введите корректный путь' unless Dir.exist?(path)
18
+
19
+ File.expand_path(path)
20
+ end
21
+
22
+ def self.set_dl_path
23
+ loop do
24
+ ask_sentence = 'Введите путь в желаемую директорию скачиваемых аудиозаписей'
25
+ default_path = File.join(ENV['HOME'], '/Music')
26
+
27
+ if Dir.exist?(default_path)
28
+ ask_sentence << " (нажмите Enter, чтобы установить #{default_path.magenta} в качестве этой директории)"
29
+ else
30
+ default_path = nil
31
+ end
32
+
33
+ path = UI.ask(ask_sentence + ':')
34
+
35
+ # If user press enter, set path to default
36
+ path = default_path if path == '' && default_path
37
+
38
+ if Dir.exist?(path)
39
+ Cfg[:dl_path] = File.expand_path(path)
40
+ UI.say "#{'Директория загружаемых аудиотреков установлена по пути'.green} #{path.magenta}"
41
+ break
42
+ else
43
+ UI.error 'Такой папки не существует! Пожалуйста, введите корректный путь'
44
+ end
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,24 @@
1
+ module Vnehm
2
+
3
+ ##
4
+ # iTunes playlist primitive
5
+
6
+ class Playlist
7
+
8
+ attr_reader :name
9
+
10
+ def initialize(name)
11
+ @name = name.chomp
12
+ end
13
+
14
+ def add_track(track)
15
+ UI.say 'Adding to iTunes'
16
+ AppleScript.add_track_to_playlist(track.file_path, @name)
17
+ end
18
+
19
+ def to_s
20
+ @name
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,66 @@
1
+ require 'vnehm/applescript'
2
+ require 'vnehm/playlist'
3
+
4
+ module Vnehm
5
+
6
+ ##
7
+ # Playlist manager works with iTunes playlists
8
+
9
+ module PlaylistManager
10
+
11
+ def self.default_playlist
12
+ default_user_playlist || music_master_library if OS.mac?
13
+ end
14
+
15
+ ##
16
+ # Checks path for existence and returns it if exists
17
+
18
+ def self.get_playlist(playlist_name)
19
+ if AppleScript.list_of_playlists.include? playlist_name
20
+ Playlist.new(playlist_name)
21
+ else
22
+ UI.term 'Такого плейлиста не существует. Введите корректное название'
23
+ end
24
+ end
25
+
26
+ def self.set_playlist
27
+ loop do
28
+ playlist = UI.ask('Введите имя плейлиста iTunes, в который вы ' \
29
+ 'хотите добавлять треки (нажмите Enter, чтобы ' \
30
+ 'не добавлять аудиозаписи в плейлист, ' \
31
+ 'а в музыкальную медиатеку iTunes)')
32
+
33
+ # If entered nothing, unset iTunes playlist
34
+ if playlist == ''
35
+ Cfg[:playlist] = nil
36
+ UI.success 'Треки будут добавляться в музыкальную медиатеку iTunes'
37
+ break
38
+ end
39
+
40
+ if AppleScript.list_of_playlists.include? playlist
41
+ Cfg[:playlist] = playlist
42
+ UI.say 'Аудиозаписи будут добавляться в плейлист '.green +
43
+ playlist.magenta
44
+ break
45
+ else
46
+ UI.error 'Такого плейлиста не существует. Введите корректное название'
47
+ end
48
+ end
49
+ end
50
+
51
+
52
+ module_function
53
+
54
+ def default_user_playlist
55
+ Playlist.new(Cfg[:playlist]) unless Cfg[:playlist].nil?
56
+ end
57
+
58
+ ##
59
+ # Music master library is main iTunes music library
60
+
61
+ def music_master_library
62
+ Playlist.new(AppleScript.music_master_library)
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,33 @@
1
+ require 'cgi'
2
+
3
+ module Vnehm
4
+
5
+ module TokenManager
6
+
7
+ def self.token
8
+ Cfg['token'] if Cfg.exist?
9
+ end
10
+
11
+ def self.authorize
12
+ auth_url = Client.authorization_url
13
+ UI.say "1. Переходите по этой ссылке с помощью вашего браузера: #{auth_url.magenta}"
14
+ UI.say "2. Жмите #{'Разрешить'.green}"
15
+ UI.say '3. Скопируйте ссылку из адресной строки вашего браузера сюда:'
16
+ uri = UI.ask
17
+
18
+ uri = URI(uri)
19
+ hash = CGI::parse(uri.fragment)
20
+
21
+ if hash['access_token']
22
+ Cfg['token'] = hash['access_token'].first
23
+ else
24
+ UI.term 'Вы ввели неверную ссылку! ' \
25
+ 'Пожалуйста, авторизируйтесь ещё раз и перепроверьте введеные данные'
26
+ end
27
+
28
+ UI.success 'Вы успешно авторизованы!'
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,51 @@
1
+ module Vnehm
2
+
3
+ # Primitive for SoundCloud track
4
+
5
+ class Track
6
+
7
+ attr_reader :hash
8
+
9
+ def initialize(hash)
10
+ @hash = hash
11
+ end
12
+
13
+ def artist
14
+ CGI::unescapeHTML(@hash.artist)
15
+ end
16
+
17
+ def duration
18
+ seconds = @hash['duration']
19
+
20
+ time = Time.at(seconds)
21
+ time -= time.utc_offset
22
+
23
+ time.hour > 0 ? time.strftime("%H:%M:%S") : time.strftime("%M:%S")
24
+ end
25
+
26
+ def file_name
27
+ "#{full_name.tr(',./\\\'$%"', '')}.mp3"
28
+ end
29
+
30
+ def file_path
31
+ File.join(ENV['dl_path'], file_name)
32
+ end
33
+
34
+ def full_name
35
+ "#{artist} - #{title}"
36
+ end
37
+
38
+ def id
39
+ @hash.aid
40
+ end
41
+
42
+ def title
43
+ CGI::unescapeHTML(@hash.title)
44
+ end
45
+
46
+ def url
47
+ @hash.url
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,86 @@
1
+ require 'taglib'
2
+
3
+ require 'vnehm/track'
4
+
5
+ module Vnehm
6
+
7
+ class TrackManager
8
+
9
+ def initialize(options)
10
+ setup_environment(options)
11
+ end
12
+
13
+ def process_tracks(tracks)
14
+ tracks.reverse_each do |track|
15
+ UI.newline
16
+ dl(track)
17
+ tag(track)
18
+ @playlist.add_track(track) if @playlist
19
+ UI.newline
20
+ end
21
+ UI.success 'Done!'
22
+ end
23
+
24
+ def tracks(limit, offset)
25
+ UI.term "Неверное значение опции 'limit'\nОно должно быть больше 0" if limit <= 0
26
+ UI.term "Неверное значении опции 'offset'\nОно должно быть больше или равно 0" if offset < 0
27
+
28
+ tracks = Client.tracks(limit, offset)
29
+ return nil if tracks.empty?
30
+
31
+ tracks.map! { |hash| Track.new(hash) }
32
+ end
33
+
34
+ def search(query, limit, offset)
35
+ UI.term "Неверное значение опции 'limit'\nОно должно быть больше 0" if limit <= 0
36
+ UI.term "Неверное значении опции 'offset'\nОно должно быть больше или равно 0" if offset < 0
37
+
38
+ found = Client.search(query, limit, offset)
39
+ return nil if found.empty?
40
+
41
+ found.shift # Deleting first item - 'count'
42
+
43
+ found.map! { |hash| Track.new(hash) }
44
+ end
45
+
46
+ private
47
+
48
+ def dl(track)
49
+ # Downloading track
50
+ UI.say 'Загрузка ' + track.full_name
51
+ `curl -# -o \"#{track.file_path}\" -L \"#{track.url}\"`
52
+ end
53
+
54
+ def tag(track)
55
+ TagLib::MPEG::File.open(track.file_path) do |file|
56
+ tag = file.id3v2_tag
57
+ tag.artist = track.artist
58
+ tag.title = track.title
59
+
60
+ file.save
61
+ end
62
+ end
63
+
64
+ def setup_environment(options)
65
+ # Setting up download path
66
+ temp_path = options[:to]
67
+ dl_path = temp_path ? PathManager.get_path(temp_path) : PathManager.default_dl_path
68
+ if dl_path
69
+ ENV['dl_path'] = dl_path
70
+ else
71
+ UI.error "Вы не указали путь, куда должны скачиваться треки!"
72
+ UI.say "Укажите его через команду #{'vnehm configure'.yellow} " \
73
+ " или используйте опцию #{'to ПУТЬ'.yellow}"
74
+ UI.term
75
+ end
76
+
77
+ # Setting up iTunes playlist
78
+ @playlist = nil
79
+ if options[:dl] != 'yes' && OS.mac?
80
+ playlist_name = options[:pl]
81
+ @playlist = playlist_name ? PlaylistManager.get_playlist(playlist_name) : PlaylistManager.default_playlist
82
+ end
83
+ end
84
+
85
+ end
86
+ end