budik 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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