365 lines
11 KiB
Lua
Executable File
365 lines
11 KiB
Lua
Executable File
----------------------------------------------------------------------------
|
|
--- Wallpaper changer module
|
|
--
|
|
-- @author Gerome Matilla <gerome.matilla07@gmail.com>
|
|
-- @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
|
|
)
|