(define-module (tw services media) #:use-module (gnu) #:use-module (gnu packages video) #:use-module (gnu services) #:use-module (gnu services configuration) #:use-module (gnu services mcron) #:use-module (gnu services shepherd) #:use-module (guix gexp) #:use-module (guix packages) #:export (yt-dlp-service-type yt-dlp-configuration)) (define (package-or-false? value) (or (package? value) (eq? #f value))) (define (string-or-gexp? value) (or (string? value) (gexp? value))) (define random-time-every-second-night #~(lambda (now) (let* ((even-day (if (even? (tm:yday (localtime now))) now (next-day-from now))) (deterministic-value (number->string (tm:yday (localtime even-day))))) ;; Between 1 and 6 a.m., randomly, but deterministically for each ;; day (to avoid runs being skipped in case mcron is restarted). ;; (random 6 (seed->random-state yday)) isn't chaotic enough. (next-minute-from (next-hour-from even-day (list (1+ (string-hash deterministic-value 6)))) (list (string-hash deterministic-value 60)))))) (define-configuration/no-serialization yt-dlp-configuration (media-directory string "The directory in which to store downloaded media. The service expects a @code{.yt-dlp} config directory inside this one.") (yt-dlp (package-or-false yt-dlp) "The yt-dlp package to use; or @code{#false} to use the yt-dlp script inside the config directory.") (schedule (string-or-gexp random-hour-every-second-night) "The mcron schedule on which to run the download script. By default, picks a random hour between 01:00 and 06:00 every second night.") (user (string "root") "The Unix user name to run the script as.") (group (string "root") "The Unix group name to run the script as.")) (define (yt-dlp-cronjob config) (list (let* ((yt-dlp-package (yt-dlp-configuration-yt-dlp config)) (yt-dlp-executable (if yt-dlp-package (file-append yt-dlp-package "/bin/yt-dlp") (string-append (yt-dlp-configuration-media-directory config) "/.yt-dlp/yt-dlp")))) #~(job #$(yt-dlp-configuration-schedule config) (lambda () (chdir #$(yt-dlp-configuration-media-directory config)) ;; `setgid' first while we're still root (setgid (group:gid (getgr #$(yt-dlp-configuration-group config)))) (setuid (passwd:uid (getpw #$(yt-dlp-configuration-user config)))) (execl #$yt-dlp-executable "yt-dlp" "--ignore-config" "--config-location" ".yt-dlp")) ;; Human-readable string for `herd schedule mcron'. (format #f "~a --ignore-config --config-location ~a/.yt-dlp" #$yt-dlp-executable ; this may be a `', so #$ it directly #$(yt-dlp-configuration-media-directory config)))))) (define yt-dlp-service-type (service-type (name 'yt-dlp) (extensions (list (service-extension mcron-service-type yt-dlp-cronjob))) (description "Trigger yt-dlp on a schedule to download videos from YouTube.")))