budik 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.
@@ -0,0 +1,39 @@
1
+ ---
2
+ os: rpi
3
+ lang: en
4
+ player:
5
+ omxplayer:
6
+ default_volume: -2100
7
+ path: omxplayer
8
+ volume_step_secs: 3
9
+ player: omxplayer
10
+ vlc:
11
+ default_volume: 128
12
+ fullscreen: true
13
+ path: vlc
14
+ rc_host: localhost
15
+ rc_port: 50000
16
+ volume_fadein_secs: 0.125
17
+ volume_step: 1.0
18
+ wait_secs_after_run: 5
19
+ wait_secs_if_http: 3
20
+ rng:
21
+ hwrng:
22
+ source: /dev/random
23
+ method: hwrng
24
+ random.org:
25
+ apikey: ""
26
+ sources:
27
+ download:
28
+ device: /dev/sda
29
+ dir: /mnt/.budik/downloads/
30
+ method: remove
31
+ mount: "udisksctl mount -b $partition"
32
+ partition: /dev/sda1
33
+ sleep: ""
34
+ unmount: "udisksctl unmount -b $partition"
35
+ path: ~/.budik/sources.yml
36
+ tv:
37
+ available: true
38
+ use_if_no_video: true
39
+ wait_secs_after_on: 15
@@ -0,0 +1,39 @@
1
+ ---
2
+ os: windows
3
+ lang: en
4
+ player:
5
+ omxplayer:
6
+ default_volume: -2100
7
+ path: omxplayer
8
+ volume_step_secs: 3
9
+ player: vlc
10
+ vlc:
11
+ default_volume: 128
12
+ fullscreen: true
13
+ path: vlc
14
+ rc_host: localhost
15
+ rc_port: 50000
16
+ volume_fadein_secs: 0.125
17
+ volume_step: 1.0
18
+ wait_secs_after_run: 5
19
+ wait_secs_if_http: 3
20
+ rng:
21
+ hwrng:
22
+ source: ""
23
+ method: rand
24
+ random.org:
25
+ apikey: ""
26
+ sources:
27
+ download:
28
+ device: ""
29
+ dir: ~/.budik/downloads/
30
+ method: stream
31
+ mount: ""
32
+ partition: ""
33
+ sleep: ""
34
+ unmount: ""
35
+ path: ~/.budik/sources.yml
36
+ tv:
37
+ available: false
38
+ use_if_no_video: false
39
+ wait_secs_after_on: 0
@@ -0,0 +1,34 @@
1
+ ---
2
+ default:
3
+ - "https://www.youtube.com/watch?v=oHg5SJYRHA0"
4
+ #
5
+ # Syntax:
6
+ # =======
7
+ #
8
+ # category1:
9
+ # subcategory1:
10
+ # - "path"
11
+ # -
12
+ # - "path1"
13
+ # - "path2"
14
+ # - name:
15
+ # - "path"
16
+ # - name:
17
+ # - "path1"
18
+ # - "path2"
19
+ # subcategory2:
20
+ # - "path"
21
+ # category2:
22
+ # subcategory1:
23
+ # subsubcategory1:
24
+ # - "path1"
25
+ # - "path2"
26
+ # subsubcategory2:
27
+ # - "path3"
28
+ # subcategory2:
29
+ # - "path4"
30
+ # category:
31
+ # - "path"
32
+ # another_category:
33
+ # category:
34
+ # - "path2"
data/lib/budik.rb ADDED
@@ -0,0 +1,152 @@
1
+ # = budik.rb
2
+ # This file contains definitions of the application's command line
3
+ # interface.
4
+ #
5
+ # == Contact
6
+ #
7
+ # Author:: Petr Schmied (mailto:jblack@paworld.eu)
8
+ # Website:: http://www.paworld.eu
9
+ # Date:: September 19, 2015
10
+
11
+ require 'colorize'
12
+ require 'commander'
13
+ require 'date'
14
+ require 'fileutils'
15
+ require 'json'
16
+ require 'net/http'
17
+ require 'open3'
18
+ require 'r18n-core'
19
+ require 'singleton'
20
+ require 'socket'
21
+ require 'sys/uname'
22
+ require 'terminal-table'
23
+ require 'uri'
24
+ require 'ya2yaml'
25
+ require 'yaml'
26
+ require 'youtube_addy'
27
+ require 'youtube-dl.rb'
28
+
29
+ require 'budik/command'
30
+ require 'budik/config'
31
+ require 'budik/devices'
32
+ require 'budik/io'
33
+ require 'budik/player'
34
+ require 'budik/rng'
35
+ require 'budik/sources'
36
+ require 'budik/storage'
37
+ require 'budik/version'
38
+
39
+ # 'Budik' is an alarm clock that randomly plays an item from your media
40
+ # collection (local or YouTube).
41
+ module Budik
42
+ # 'Budik' class describes application's command line interface.
43
+ class Budik
44
+ include Commander::Methods
45
+
46
+ # Loads strings in language specified in application's options.
47
+ def initialize
48
+ @strings = Config.instance.lang.budik
49
+
50
+ @str_config = @strings.commands.config
51
+ @str_run = @strings.commands.run
52
+ @str_sources = @strings.commands.sources
53
+ @str_translate = @strings.commands.translate
54
+ end
55
+
56
+ # Describes application's command line interface. Runs the application.
57
+ def run
58
+ program :name, 'Budik'
59
+ program :version, VERSION
60
+ program :description, @strings.description
61
+
62
+ commands
63
+
64
+ default_command :run
65
+
66
+ run!
67
+ end
68
+
69
+ private
70
+
71
+ # List of commands
72
+ def commands
73
+ command_config(@str_config.options)
74
+ command_run(@str_run.options)
75
+ command_sources(@str_sources.options)
76
+ command_translate
77
+ end
78
+
79
+ # Describes and runs command 'config'.
80
+ #
81
+ # - *Args*:
82
+ # - +str_opts+ -> Command options' strings
83
+ #
84
+ def command_config(str_opts)
85
+ command :config do |c|
86
+ c.syntax = 'budik config [options]'
87
+ c.summary = @str_config.summary
88
+ c.description = @str_config.description
89
+ c.option '-r', '--reset', str_opts.reset
90
+ c.action { |_args, opts| Command.new(:config, opts) }
91
+ end
92
+ end
93
+
94
+ # Describes and runs command 'run'.
95
+ #
96
+ # - *Args*:
97
+ # - +str_opts+ -> Command options' strings
98
+ #
99
+ def command_run(str_opts)
100
+ command :run do |c|
101
+ c.syntax = 'budik run [options]'
102
+ c.summary = @str_run.summary
103
+ c.description = @str_run.description
104
+ command_run_options(c, str_opts)
105
+ c.action { |_args, opts| Command.new(:run, opts) }
106
+ end
107
+ end
108
+
109
+ # Describes options for command 'run'.
110
+ #
111
+ # - *Args*:
112
+ # - +c+ -> Ruby Commander's object
113
+ # - +str_opts+ -> Command options' strings
114
+ #
115
+ def command_run_options(c, str_opts)
116
+ c.option '-c', '--categories [string]', String, str_opts.categories
117
+ c.option '-d', '--dl-method [string]', String, str_opts.dl_method
118
+ c.option '-n', '--number [integer]', Integer, str_opts.number
119
+ c.option '-p', '--player [string]', String, str_opts.player
120
+ c.option '-r', '--rng [string]', String, str_opts.rng
121
+ end
122
+
123
+ # Describes and runs command 'sources'.
124
+ #
125
+ # - *Args*:
126
+ # - +str_opts+ -> Command options' strings
127
+ #
128
+ def command_sources(str_opts)
129
+ command :sources do |c|
130
+ c.syntax = 'budik sources [options]'
131
+ c.summary = @str_sources.summary
132
+ c.description = @str_sources.description
133
+ c.option '-c', '--categories [string]', String, str_opts.categories
134
+ c.option '-d', '--download', str_opts.download
135
+ c.option '-e', '--edit', str_opts.download
136
+ c.action { |_args, opts| Command.new(:sources, opts) }
137
+ end
138
+ end
139
+
140
+ # Describes and runs command 'translate'.
141
+ def command_translate
142
+ command :translate do |c|
143
+ c.syntax = 'budik translate [options]'
144
+ c.summary = @str_translate.summary
145
+ c.description = @str_translate.description
146
+ c.action { |args, _opts| Command.new(:translate, args) }
147
+ end
148
+ end
149
+ end
150
+
151
+ Budik.new.run if $PROGRAM_NAME == __FILE__
152
+ end
@@ -0,0 +1,199 @@
1
+ # = command.rb
2
+ # This file contains definitions of the application's commands.
3
+ #
4
+ # == Contact
5
+ #
6
+ # Author:: Petr Schmied (mailto:jblack@paworld.eu)
7
+ # Website:: http://www.paworld.eu
8
+ # Date:: September 19, 2015
9
+
10
+ module Budik
11
+ # 'Command' class holds definitions of CLI commands.
12
+ class Command
13
+ # Loads options, sources and strings.
14
+ # Runs command and passes specified options to it.
15
+ #
16
+ # - *Args*:
17
+ # - +command+ -> Command to run (Symbol)
18
+ # - +opts+ -> Command line options (Ruby Commander's object)
19
+ #
20
+ def initialize(command, opts)
21
+ @options = Config.instance.options
22
+ @sources = Config.instance.sources
23
+ @strings = Config.instance.lang.command
24
+
25
+ send(command, opts)
26
+ end
27
+
28
+ private
29
+
30
+ # Defines command 'config'.
31
+ # Opens configuration file for editing or resets it.
32
+ #
33
+ # - *Args*:
34
+ # - +opts+ -> Command line options
35
+ #
36
+ def config(opts)
37
+ if opts.reset
38
+ Config.instance.reset
39
+ else
40
+ Config.instance.edit
41
+ end
42
+ end
43
+
44
+ # Runs alarm or falls back if an error is encountered.
45
+ #
46
+ # - *Args*:
47
+ # - +opts+ -> Command line options
48
+ #
49
+ def run(opts)
50
+ run_alarm(opts)
51
+ rescue
52
+ run_alarm_fallback
53
+ end
54
+
55
+ # Defines command 'run'.
56
+ # Runs alarm.
57
+ #
58
+ # - *Args*:
59
+ # - +opts+ -> Command line options
60
+ #
61
+ def run_alarm(opts)
62
+ sources = Sources.instance
63
+ storage = Storage.instance
64
+ devices = Devices.instance
65
+ rng = Rng.new
66
+ io = IO.instance
67
+ player = Player.instance
68
+
69
+ run_use_cli_opts(opts)
70
+ source = run_prepare(opts, sources, devices, rng, io)
71
+ run_download(source, storage.method, storage)
72
+ run_play(source, devices, player, storage)
73
+ end
74
+
75
+ # Outputs bell code 50 times in 1.2s intervals.
76
+ def run_alarm_fallback
77
+ 50.times do
78
+ puts "\a"
79
+ sleep 1.2
80
+ end
81
+ end
82
+
83
+ # Applies command line options (player, rng, dl_method)
84
+ #
85
+ # - *Args*:
86
+ # - +opts+ -> Command line options
87
+ #
88
+ def run_use_cli_opts(opts)
89
+ @options['player']['player'] = opts.player if opts.player
90
+ @options['rng']['method'] = opts.rng if opts.rng
91
+ return unless opts.dl_method
92
+ @options['sources']['download']['method'] = opts.dl_method
93
+ end
94
+
95
+ # Parses sources, applies category modifiers, mounts device if needed,
96
+ # generates random number and uses it to return source.
97
+ #
98
+ # - *Args*:
99
+ # - +opts+ -> Command line options
100
+ # - +sources+ -> Sources class instance
101
+ # - +devices+ -> Devices class instance
102
+ # - +rng+ -> Rng class instance
103
+ # - +io+ -> IO class instance
104
+ # - *Returns*:
105
+ # - Source (Hash)
106
+ #
107
+ def run_prepare(opts, sources, devices, rng, io)
108
+ sources.parse(@sources)
109
+ mods = opts.categories ? sources.parse_mods(opts.categories) : nil
110
+ sources.apply_mods(mods) if mods
111
+
112
+ devices.storage_mount
113
+ number = opts.number || rng.generate(sources.count)
114
+ source = sources.get(number)
115
+
116
+ puts io.run_info_table(source, number)
117
+ source
118
+ end
119
+
120
+ # Downloads source if needed.
121
+ #
122
+ # - *Args*:
123
+ # - +source+ -> Source (Hash)
124
+ # - +dl_method+ -> Download method (string: keep, remove or stream)
125
+ # - +storage+ -> Storage class instance
126
+ #
127
+ def run_download(source, dl_method, storage)
128
+ storage.download_sources(source) unless dl_method == 'stream'
129
+ end
130
+
131
+ # Turns on tv if needed.
132
+ # Plays source or falls back if player unexpectedly exits too soon.
133
+ # If needed, turns off TV, removes source and unmounts storage.
134
+ #
135
+ # - *Args*:
136
+ # - +sources+ -> Sources class instance
137
+ # - +devices+ -> Devices class instance
138
+ # - +player+ -> Player class instance
139
+ # - +storage+ -> Storage class instance
140
+ #
141
+ def run_play(source, devices, player, storage)
142
+ devices.tv_on
143
+
144
+ start = Time.now
145
+ player.play(source)
146
+ run_alarm_fallback if Time.now - start < 5.0
147
+
148
+ devices.tv_off
149
+ storage.remove_sources(source)
150
+ devices.storage_unmount
151
+ sleep 5
152
+ devices.storage_sleep
153
+ end
154
+
155
+ # Defines command 'sources'
156
+ # Opens sources file for editing or prepares sources
157
+ # for viewing or downloading.
158
+ #
159
+ # - *Args*:
160
+ # - +opts+ -> Command line options
161
+ #
162
+ def sources(opts)
163
+ if opts.edit
164
+ path = File.expand_path(Config.instance.options['sources']['path'])
165
+ Config.instance.open_file(path)
166
+ else
167
+ sources = Sources.instance
168
+ sources.parse(@sources)
169
+ sources_list_dl(sources, opts)
170
+ end
171
+ end
172
+
173
+ # Applies category modifiers and lists or download sources.
174
+ #
175
+ # - *Args*:
176
+ # - +sources+ -> Sources class instance
177
+ # - +opts+ -> Command line options
178
+ #
179
+ def sources_list_dl(sources, opts)
180
+ mods = opts.categories ? sources.parse_mods(opts.categories) : nil
181
+ sources.apply_mods(mods) if mods
182
+
183
+ if opts.download
184
+ Storage.instance.download_sources
185
+ else
186
+ IO.instance.sources_print(sources.sources)
187
+ end
188
+ end
189
+
190
+ # Creates and/or opens language file for translation.
191
+ #
192
+ # - *Args*:
193
+ # - +args+ -> Command line arguments, first used as language code
194
+ #
195
+ def translate(args)
196
+ Config.instance.translate(args.first)
197
+ end
198
+ end
199
+ end