This repository was archived by the owner on Oct 31, 2023. It is now read-only.
forked from material-shell/material-awesome
-
-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathdynamic-wallpaper.lua
executable file
·364 lines (297 loc) · 10.9 KB
/
dynamic-wallpaper.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
----------------------------------------------------------------------------
--- Wallpaper changer module
--
-- @author Gerome Matilla <[email protected]>
-- @copyright 2019 Gerome Matilla
-- @module wallchange
--
--- Nevermind this. Do what you want.
----------------------------------------------------------------------------
-- This module changes wallpaper based on declared time
-- It checks the difference between the current time and the next scheduled time
-- Then convert it to seconds to set it as a timeout value
-- Limitations:
-- Timeout paused when laptop/pc is suspended or in sleep mode, and there's probably some bugs too so whatever
local awful = require('awful')
local gears = require('gears')
local beautiful = require('beautiful')
local filesystem = gears.filesystem
local config = require('configuration.config')
-- ========================================
-- Configuration
-- Change your preference here
-- ========================================
local wall_config = {
-- Wallpaper directory. The default is:
-- local wall_config.wall_dir = os.getenv('HOME') .. 'Pictures/Wallpapers/'
wall_dir = filesystem.get_configuration_dir() .. (config.module.dynamic_wallpaper.wall_dir or 'theme/wallpapers/'),
-- If there's a picture format that awesome accepts and i missed
-- (which i probably did) feel free to add it right here
valid_picture_formats = config.module.dynamic_wallpaper.valid_picture_formats or {"jpg", "png", "jpeg"},
-- Table mapping schedule to wallpaper filename
wallpaper_schedule = config.module.dynamic_wallpaper.wallpaper_schedule or {
['00:00:00'] = 'midnight-wallpaper.jpg',
['06:22:00'] = 'morning-wallpaper.jpg',
['12:00:00'] = 'noon-wallpaper.jpg',
['17:58:00'] = 'night-wallpaper.jpg'
},
-- Don't stretch wallpaper on multihead setups if true
stretch = config.module.dynamic_wallpaper.stretch or false
}
-- ========================================
-- Processes
-- Don't touch it if it's working
-- ========================================
-- Get current time
local current_time = function()
return os.date('%H:%M:%S')
end
-- Countdown variable
-- In seconds
local the_countdown = nil
-- Parse seconds to HH:MM:SS
local function parse_to_time(seconds)
-- DST ruined me :(
--return os.date("%H:%M:%S", seconds)
local function format(str)
while #str < 2 do
str = '0' .. str
end
return str
end
local function convert(num)
return format(tostring(num))
end
local hours = convert(math.floor(seconds / 3600))
seconds = seconds - (hours * 3600)
local minutes = convert(math.floor(seconds / 60))
seconds = seconds - (minutes * 60)
local seconds = convert(math.floor(seconds))
return (hours .. ':' .. minutes .. ':' .. seconds)
end
-- Parse HH:MM:SS to seconds
local parse_to_seconds = function(time)
-- Convert HH in HH:MM:SS
local hour_sec = tonumber(string.sub(time, 1, 2)) * 3600
-- Convert MM in HH:MM:SS
local min_sec = tonumber(string.sub(time, 4, 5)) * 60
-- Get SS in HH:MM:SS
local get_sec = tonumber(string.sub(time, 7, 8))
-- Return computed seconds
return (hour_sec + min_sec + get_sec)
end
-- Get time difference
local time_diff = function(future, past)
local diff = parse_to_seconds(future) - parse_to_seconds(past)
if diff < 0 then
diff = diff + (24 * 3600) --If time difference is negative, the future is meant for tomorrow
end
return diff
end
-- Returns a table containing all file paths in a directory
local function get_dir_contents(dir)
-- Command to give list of files in directory
local dir_explore = 'find ' .. dir .. ' -printf "%f\\n"'
local lines = io.popen(dir_explore):lines() --Done synchronously because we literally can't continue without files
local files = {}
for line in lines do
table.insert(files, line)
end
return files
end
-- Returns a table of all the files that were one of the valid file formats
local function filter_files_by_format(files, valid_file_formats)
local valid_files = {}
for _, file in ipairs(files) do
for _, format in ipairs(valid_file_formats) do
if string.match(file, ".+%." .. format) ~= nil then
table.insert(valid_files, file)
break --No need to check other formats
end
end
end
return valid_files
end
-- Returns a table of files that contained any of the keywords, in the same order as the words themselves
local function find_files_containing_keywords(files, keywords)
local found_files = {}
for _, word in ipairs(keywords) do --Preserves keyword order inherently, conveniently
for _, file in ipairs(files) do
-- Check if file is word, contains word at beginning or contains word between 2 non-alphanumeric characters
if file == word or string.find(file, "^" .. word .. "[^%a]") or string.find(file, "[^%a]" .. word .. "[^%a]") then
found_files[word] = file
break --Only return 1 file per word
end
end
end
return found_files
end
-- Turn an ordered list of files into a scheduled list of files
local function auto_schedule(wall_list)
local sched = {}
for index, file in ipairs(wall_list) do
local auto_time = parse_to_time(parse_to_seconds("24:00:00") * (index - 1) / #wall_list)
sched[auto_time] = file
end
return sched
end
-- Reformat whatever schedule was specified into an actual schedule
if #wall_config.wallpaper_schedule == 0 then
local count = 0
-- Determine if empty or if manual schedule
for k, v in pairs(wall_config.wallpaper_schedule) do
count = count + 1
end
if count == 0 then --Schedule is actually empty
-- Get all pictures
local pictures = filter_files_by_format(get_dir_contents(wall_config.wall_dir), wall_config.valid_picture_formats)
--Sort pictures as sanely as possible
local function order_pictures(a, b) --Attempts to mimic default sort but numbers aren't compared as strings
if tonumber(a) ~= nil and tonumber(b) ~= nil then
return tonumber(a) < tonumber(b)
else
return a < b
end
end
table.sort(pictures, order_pictures)
wall_config.wallpaper_schedule = auto_schedule(pictures)
else --Schedule is manually timed
-- Get times as list
local ordered_times = {}
for time, _ in pairs(wall_config.wallpaper_schedule) do
table.insert(ordered_times, time)
end
-- Sort times using seconds as comparison
local function order_time_asc(a, b)
return parse_to_seconds(a) < parse_to_seconds(b)
end
table.sort(ordered_times, order_time_asc)
-- Get ordered list of keywords from ordered times
local keywords = {}
for index, time in ipairs(ordered_times) do
keywords[index] = wall_config.wallpaper_schedule[time]
end
-- Get any pictures that match keywords
local pictures = filter_files_by_format(get_dir_contents(wall_config.wall_dir), wall_config.valid_picture_formats)
pictures = find_files_containing_keywords(pictures, keywords)
-- Replace keywords with files
for index, time in ipairs(ordered_times) do
local word = wall_config.wallpaper_schedule[time]
if pictures[word] ~= nil then
wall_config.wallpaper_schedule[time] = pictures[word]
else --To avoid crashes, we'll remove entries with invalid keywords
wall_config.wallpaper_schedule[time] = nil
end
end
end
else --Schedule is list of keywords
local keywords = wall_config.wallpaper_schedule
-- Get any pictures that match keywords
local pictures = filter_files_by_format(get_dir_contents(wall_config.wall_dir), wall_config.valid_picture_formats)
pictures = find_files_containing_keywords(pictures, keywords)
-- Order files by keyword (if a file was found for the keyword)
local ordered_pictures = {}
for _, word in ipairs(keywords) do
local file = pictures[word]
if file ~= nil then
table.insert(ordered_pictures, file)
end
end
wall_config.wallpaper_schedule = auto_schedule(ordered_pictures)
end
-- Set wallpaper
local set_wallpaper = function(path)
if wall_config.stretch then
for s in screen do
-- Update wallpaper based on the data in the array
gears.wallpaper.maximized (path, s)
end
else
-- Update wallpaper based on the data in the array
gears.wallpaper.maximized (path)
end
end
-- Update wallpaper (used by the manage_timer function)
-- I think the gears.wallpaper.maximized is too fast or being ran asynchronously
-- So the wallpaper is not being updated on awesome (re)start without this timer
-- We need some delay.
-- Hey it's working, so whatever
local update_wallpaper = function(wall_name)
local wall_dir = wall_config.wall_dir .. wall_name
set_wallpaper(wall_dir)
-- Overwrite the default wallpaper
-- This is important in case we add an extra monitor
beautiful.wallpaper = wall_dir
end
-- Updates variables
local manage_timer = function()
-- Get current time
local time_now = parse_to_seconds(current_time())
local previous_time = '' --Scheduled time that should activate now
local next_time = '' --Time that should activate next
local first_time = '24:00:00' --First scheduled time registered (to be found)
local last_time = '00:00:00' --Last scheduled time registered (to be found)
-- Find previous_time
for time, wallpaper in pairs(wall_config.wallpaper_schedule) do
local parsed_time = parse_to_seconds(time)
if previous_time == '' or parsed_time > parse_to_seconds(previous_time) then
if parsed_time <= time_now then
previous_time = time
end
end
if parsed_time > parse_to_seconds(last_time) then
last_time = time
end
end
-- Previous time being blank = no scheduled time today. Therefore
-- the last time was yesterday's latest time
if previous_time == '' then
previous_time = last_time
end
--Find next_time
for time, wallpaper in pairs(wall_config.wallpaper_schedule) do
local parsed_time = parse_to_seconds(time)
if next_time == '' or parsed_time < parse_to_seconds(next_time) then
if parsed_time > time_now then
next_time = time
end
end
if parsed_time < parse_to_seconds(first_time) then
first_time = time
end
end
-- Next time being blank means that there is no scheduled times left for
-- the current day. So next scheduled time is tomorrow's first time
if next_time == '' then
next_time = first_time
end
-- Update Wallpaper
update_wallpaper(wall_config.wallpaper_schedule[previous_time])
-- Get the time difference to set as timeout for the wall_updater timer below
the_countdown = time_diff(next_time, current_time())
end
-- Update values at startup
manage_timer()
local wall_updater = gears.timer {
-- The timeout is the difference of current time and the scheduled time we set above.
timeout = the_countdown,
autostart = true,
call_now = true,
callback = function()
-- Emit signal to update wallpaper
awesome.emit_signal('module::change_wallpaper')
end
}
-- Update wallpaper here and update the timeout for the next schedule
awesome.connect_signal(
'module::change_wallpaper',
function()
--set_wallpaper(wall_dir .. wall_data[2])
-- Update values for the next specified schedule
manage_timer()
-- Update timer timeout for the next specified schedule
wall_updater.timeout = the_countdown
-- Restart timer
wall_updater:again()
end
)