diff --git a/README.md b/README.md index f1e9645..449196d 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ platform path implementation. ## Configuration Important configuration keys are created automatically in `.flac-48khz-manager.ini`. -The same file is used by both managers. +The same file is used by both managers. The implementation now reads this file as a simple-ini value directly; there is no separate configuration struct layered on top of the ini model. ```ini [manager] @@ -90,6 +90,9 @@ display-log=#t log-file=".flac-48khz-manager.log" compression-level=5 +[opus-manager] +log-file=".flac2opus-manager.log" + [mail] enabled=#f send-on-success=#f diff --git a/flac-48khz-manager.rkt b/flac-48khz-manager.rkt index 3c946ba..3839f4c 100644 --- a/flac-48khz-manager.rkt +++ b/flac-48khz-manager.rkt @@ -5,7 +5,7 @@ racket/list racket/path "private/audio.rkt" - "private/config.rkt" + "private/manager-ini.rkt" "private/convert-place.rkt" "private/cover-art.rkt" "private/fingerprint.rkt" @@ -35,13 +35,11 @@ (equal? (alist-ref/default old 'signature #f) signature) (not (equal? (alist-ref/default old 'status #f) 'error)))) -(define (file-signature path config fingerprint-proc) - (define mode (manager-config-change-detection config)) +(define (file-signature path ini fingerprint-proc) + (define mode (string-downcase (ini-ref/string ini 'manager 'change-detection "flac-taglib"))) + (define hash-algorithm (string-downcase (ini-ref/string ini 'manager 'hash-algorithm "sha256"))) (cond [(member mode '("hash" "sha256" "full-hash" "full-sha256")) - (string-append "file:" - (manager-config-hash-algorithm config) - ":" - (file-digest path (manager-config-hash-algorithm config)))] + (string-append "file:" hash-algorithm ":" (file-digest path hash-algorithm))] [(member mode '("mtime-size" "quick")) (format "mtime-size:~a:~a" (file-size path) (file-or-directory-modify-seconds path))] [(member mode '("flac" "flac-streaminfo")) @@ -66,7 +64,7 @@ (when cover (info-alm "embedded sidecar cover in ~a from ~a" relpath cover)) cover)) -(define (process-one-file ks config path relpath inspect-flac-proc fingerprint-proc convert-proc summary errors) +(define (process-one-file ks ini path relpath inspect-flac-proc fingerprint-proc convert-proc summary errors) ;; Only invokes TagLib when a sidecar cover/folder image exists in the same directory. ;; This keeps the cheap mtime/size fast path cheap for most directories, but still ;; lets the manager repair missing embedded artwork before fingerprinting. @@ -90,25 +88,25 @@ (cons 'message (exn-message e))) errors)))]) (define sample-rate (inspect-flac-proc path)) - (define signature (file-signature path config fingerprint-proc)) + (define signature (file-signature path ini fingerprint-proc)) (cond [(same-signature-state? old signature) (info-alm "unchanged fingerprint: ~a" relpath) (state-set-file! ks relpath (state-info path signature 'unchanged sample-rate '())) (values (summary-inc summary2 'unchanged) errors)] - [(> sample-rate (manager-config-max-sample-rate config)) - (if (manager-config-dry-run? config) + [(> sample-rate (ini-ref/int ini 'manager 'max-sample-rate 48000)) + (if (ini-ref/bool ini 'manager 'dry-run #f) (begin - (warn-alm "dry-run: would convert ~a from ~a Hz to ~a Hz" relpath sample-rate (manager-config-max-sample-rate config)) + (warn-alm "dry-run: would convert ~a from ~a Hz to ~a Hz" relpath sample-rate (ini-ref/int ini 'manager 'max-sample-rate 48000)) (state-set-file! ks relpath (state-info path signature 'dry-run sample-rate '())) (values (summary-inc (summary-inc summary2 'skipped) 'dry-run) errors)) (begin - (info-alm "converting ~a from ~a Hz to ~a Hz" relpath sample-rate (manager-config-max-sample-rate config)) + (info-alm "converting ~a from ~a Hz to ~a Hz" relpath sample-rate (ini-ref/int ini 'manager 'max-sample-rate 48000)) (let* ((result (convert-proc path - (manager-config-max-sample-rate config) - (manager-config-compression-level config))) + (ini-ref/int ini 'manager 'max-sample-rate 48000) + (ini-ref/int ini 'manager 'compression-level 5))) (new-sample-rate (inspect-flac-proc path)) - (new-signature (file-signature path config fingerprint-proc))) + (new-signature (file-signature path ini fingerprint-proc))) (state-set-file! ks relpath (state-info path new-signature 'converted new-sample-rate (list (cons 'old-sample-rate sample-rate) @@ -136,22 +134,25 @@ (define base-dir (filesystem-path base-directory)) (unless (directory-exists? base-dir) (raise-argument-error 'manage-flac-tree "existing directory" base-directory)) - (define config (load-manager-config base-dir)) - (setup-logging! (manager-config-log-file config) (manager-config-display-log? config)) + (define ini-file (manager-ini-file base-dir)) + (define state-file (manager-state-file base-dir)) + (define ini (load-manager-ini base-dir)) + (setup-logging! (ini-ref/path ini base-dir 'manager 'log-file ".flac-48khz-manager.log") + (ini-ref/bool ini 'manager 'display-log #t)) (info-alm "base directory: ~a" base-dir) - (info-alm "state file: ~a" (manager-config-state-file config)) - (info-alm "ini file: ~a" (manager-config-ini-file config)) - (info-alm "change detection: ~a" (manager-config-change-detection config)) - (define ks (open-manager-state (manager-config-state-file config))) + (info-alm "state file: ~a" state-file) + (info-alm "ini file: ~a" ini-file) + (info-alm "change detection: ~a" (ini-ref/string ini 'manager 'change-detection "flac-taglib")) + (define ks (open-manager-state state-file)) (define files (find-flac-files base-dir)) (define relpaths (map (lambda (p) (relpath-string base-dir p)) files)) (define summary0 (drop-removed! ks relpaths (make-empty-summary))) (define-values (summary errors) (for/fold ([summary summary0] [errors '()]) ([p (in-list files)] [rel (in-list relpaths)]) - (process-one-file ks config p rel inspect-flac-proc fingerprint-proc convert-proc summary errors))) + (process-one-file ks ini p rel inspect-flac-proc fingerprint-proc convert-proc summary errors))) (define errors* (reverse errors)) (with-handlers ([exn:fail? (lambda (e) (err-alm "mail report failed: ~a" (exn-message e)) (void))]) - (maybe-send-report-mail config summary errors*)) + (maybe-send-report-mail ini summary errors*)) (for ([line (in-list (summary->lines summary))]) (info-alm "summary: ~a" line)) summary) diff --git a/flac2opus-manager.rkt b/flac2opus-manager.rkt index be736a3..6897f3b 100644 --- a/flac2opus-manager.rkt +++ b/flac2opus-manager.rkt @@ -4,7 +4,7 @@ racket/file racket/list racket/path - "private/config.rkt" + "private/manager-ini.rkt" "private/flac2opus-state.rkt" "private/fingerprint.rkt" "private/hash.rkt" @@ -26,11 +26,11 @@ (define (manager-admin-relpath? relpath) (member relpath manager-admin-relpaths)) -(define (source-file-signature path config) - (define mode (manager-config-change-detection config)) +(define (source-file-signature path ini) + (define mode (string-downcase (ini-ref/string ini 'manager 'change-detection "flac-taglib"))) + (define hash-algorithm (string-downcase (ini-ref/string ini 'manager 'hash-algorithm "sha256"))) (cond [(member mode '("hash" "sha256" "full-hash" "full-sha256")) - (string-append "file:" (manager-config-hash-algorithm config) ":" - (file-digest path (manager-config-hash-algorithm config)))] + (string-append "file:" hash-algorithm ":" (file-digest path hash-algorithm))] [(flac-path? path) (cond [(member mode '("flac" "flac-streaminfo")) (string-append "flac-streaminfo:" (flac-streaminfo-fingerprint path))] @@ -103,7 +103,7 @@ (cons 'kbps kbps)) extra)) -(define (process-one-source-file ks config target-dir path relpath target-relpath kbps convert-proc summary errors) +(define (process-one-source-file ks ini target-dir path relpath target-relpath kbps convert-proc summary errors) (define target-path (target-path-for target-dir target-relpath)) (define old (flac2opus-state-get-file ks relpath #f)) (cond [(same-quick-state? old path target-path target-relpath kbps) @@ -125,13 +125,13 @@ (cons (list (cons 'file relpath) (cons 'message (exn-message e))) errors)))]) - (define signature (source-file-signature path config)) + (define signature (source-file-signature path ini)) (cond [(same-signature-state? old signature target-path target-relpath kbps) (info-alm "unchanged fingerprint: ~a" relpath) (flac2opus-state-set-file! ks relpath (state-info path target-relpath signature 'unchanged kbps '())) (values (summary-inc summary2 'unchanged) errors)] - [(manager-config-dry-run? config) + [(ini-ref/bool ini 'manager 'dry-run #f) (warn-alm "dry-run: would mirror ~a -> ~a" relpath target-relpath) (flac2opus-state-set-file! ks relpath (state-info path target-relpath signature 'dry-run kbps '())) @@ -181,15 +181,18 @@ (unless (directory-exists? source-dir) (raise-argument-error 'manage-flac2opus-tree "existing source directory" source-directory)) (make-directory* target-dir) - (define config (load-manager-config source-dir)) - (setup-logging! (manager-config-log-file-opus config) (manager-config-display-log? config)) + (define ini-file (manager-ini-file source-dir)) + (define state-file (manager-state-file source-dir)) + (define ini (load-manager-ini source-dir)) + (setup-logging! (ini-ref/path ini source-dir 'opus-manager 'log-file ".flac2opus-manager.log") + (ini-ref/bool ini 'manager 'display-log #t)) (info-alm "flac2opus source directory: ~a" source-dir) (info-alm "flac2opus target directory: ~a" target-dir) - (info-alm "state file: ~a" (manager-config-state-file config)) - (info-alm "ini file: ~a" (manager-config-ini-file config)) - (info-alm "change detection: ~a" (manager-config-change-detection config)) + (info-alm "state file: ~a" state-file) + (info-alm "ini file: ~a" ini-file) + (info-alm "change detection: ~a" (ini-ref/string ini 'manager 'change-detection "flac-taglib")) (info-alm "opus bitrate: ~a kbps" kbps) - (define ks (open-flac2opus-state (manager-config-state-file config))) + (define ks (open-flac2opus-state state-file)) (define files (find-source-files source-dir)) (define relpaths (map (lambda (p) (source-relpath-string source-dir p)) files)) (define target-relpaths (map target-relpath-for relpaths)) @@ -198,10 +201,10 @@ (for/fold ([summary summary0] [errors '()]) ([p (in-list files)] [relpath (in-list relpaths)] [target-relpath (in-list target-relpaths)]) - (process-one-source-file ks config target-dir p relpath target-relpath kbps convert-proc summary errors))) + (process-one-source-file ks ini target-dir p relpath target-relpath kbps convert-proc summary errors))) (define errors* (reverse errors)) (with-handlers ([exn:fail? (lambda (e) (err-alm "mail report failed: ~a" (exn-message e)) (void))]) - (maybe-send-report-mail config summary errors* + (maybe-send-report-mail ini summary errors* #:manager-name "FLAC to Opus manager" #:result-label "converted/copied")) (for ([line (in-list (summary->lines summary))]) (info-alm "summary: ~a" line)) diff --git a/private/config.rkt b/private/config.rkt deleted file mode 100644 index 02862c6..0000000 --- a/private/config.rkt +++ /dev/null @@ -1,110 +0,0 @@ -#lang racket/base - -(require racket/file - racket/path - simple-ini - "util.rkt") - -(provide manager-config? - manager-config-base-dir - manager-config-ini-file - manager-config-state-file - manager-config-log-file - manager-config-log-file-opus - manager-config-max-sample-rate - manager-config-hash-algorithm - manager-config-change-detection - manager-config-dry-run? - manager-config-display-log? - manager-config-compression-level - manager-config-mail-enabled? - manager-config-mail-send-on-success? - manager-config-mail-send-on-error? - manager-config-mail-host - manager-config-mail-port - manager-config-mail-tls? - manager-config-mail-username - manager-config-mail-password - manager-config-mail-from - manager-config-mail-to - manager-config-mail-cc - manager-config-mail-bcc - manager-config-mail-subject-prefix - load-manager-config - ensure-default-config!) - -(struct manager-config - (base-dir ini-file state-file log-file log-file-opus max-sample-rate hash-algorithm change-detection dry-run? - display-log? compression-level mail-enabled? mail-send-on-success? - mail-send-on-error? mail-host mail-port mail-tls? mail-username - mail-password mail-from mail-to mail-cc mail-bcc mail-subject-prefix) - #:transparent) - -(define (default-log-file base-dir) - (build-path base-dir ".flac-48khz-manager.log")) - -(define (default-ini base-dir) - (build-path base-dir ".flac-48khz-manager.ini")) - -(define (default-state base-dir) - (build-path base-dir ".music-info.db")) - -(define (ensure-default-config! ini-file) - (unless (file-exists? ini-file) - (define ini (make-ini)) - (ini-set! ini 'manager 'max-sample-rate 48000) - (ini-set! ini 'manager 'hash-algorithm "sha256") - (ini-set! ini 'manager 'change-detection "flac-taglib") - (ini-set! ini 'manager 'dry-run #f) - (ini-set! ini 'manager 'display-log #t) - (ini-set! ini 'manager 'log-file ".flac-48khz-manager.log") - (ini-set! ini 'manager 'compression-level 5) - (ini-set! ini 'mail 'enabled #f) - (ini-set! ini 'mail 'send-on-success #f) - (ini-set! ini 'mail 'send-on-error #t) - (ini-set! ini 'mail 'host "") - (ini-set! ini 'mail 'port 25) - (ini-set! ini 'mail 'tls #f) - (ini-set! ini 'mail 'username "") - (ini-set! ini 'mail 'password "") - (ini-set! ini 'mail 'from "") - (ini-set! ini 'mail 'to "") - (ini-set! ini 'mail 'cc "") - (ini-set! ini 'mail 'bcc "") - (ini-set! ini 'mail 'subject-prefix "[flac-48khz-manager]") - (ini->file ini ini-file))) - -(define (resolve-log-file base-dir v) - (define p (string-value v ".flac-48khz-manager.log")) - (cond [(path-string? p) - (define bp (string->path p)) - (if (absolute-path? bp) bp (build-path base-dir bp))] - [else (default-log-file base-dir)])) - -(define (load-manager-config base-dir*) - (define base-dir (simple-form-path base-dir*)) - (define ini-file (default-ini base-dir)) - (ensure-default-config! ini-file) - (define ini (file->ini ini-file)) - (define log-file (resolve-log-file base-dir (ini-get ini 'manager 'log-file ".flac-48khz-manager.log"))) - (define log-file-opus (resolve-log-file base-dir (ini-get ini 'opus-manager 'log-file ".flac2opus-manager.log"))) - (manager-config base-dir ini-file (default-state base-dir) log-file log-file-opus - (int-value (ini-get ini 'manager 'max-sample-rate 48000) 48000) - (string-downcase (string-value (ini-get ini 'manager 'hash-algorithm "sha256") "sha256")) - (string-downcase (string-value (ini-get ini 'manager 'change-detection "flac-taglib") "flac-taglib")) - (bool-value (ini-get ini 'manager 'dry-run #f) #f) - (bool-value (ini-get ini 'manager 'display-log #t) #t) - (int-value (ini-get ini 'manager 'compression-level 5) 5) - (bool-value (ini-get ini 'mail 'enabled #f) #f) - (bool-value (ini-get ini 'mail 'send-on-success #f) #f) - (bool-value (ini-get ini 'mail 'send-on-error #t) #t) - (string-value (ini-get ini 'mail 'host "") "") - (int-value (ini-get ini 'mail 'port 25) 25) - (bool-value (ini-get ini 'mail 'tls #f) #f) - (string-value (ini-get ini 'mail 'username "") "") - (string-value (ini-get ini 'mail 'password "") "") - (string-value (ini-get ini 'mail 'from "") "") - (split-addresses (ini-get ini 'mail 'to "")) - (split-addresses (ini-get ini 'mail 'cc "")) - (split-addresses (ini-get ini 'mail 'bcc "")) - (string-value (ini-get ini 'mail 'subject-prefix "[flac-48khz-manager]") "[flac-48khz-manager]"))) diff --git a/private/mail.rkt b/private/mail.rkt index b782ab9..0eb93ba 100644 --- a/private/mail.rkt +++ b/private/mail.rkt @@ -4,7 +4,7 @@ racket/string racket/tcp net/base64 - "config.rkt" + "manager-ini.rkt" "report.rkt") (provide maybe-send-report-mail @@ -113,22 +113,22 @@ (close-input-port in) (close-output-port out)))) -(define (maybe-send-report-mail config summary errors +(define (maybe-send-report-mail ini summary errors #:manager-name [manager-name "FLAC 48 kHz manager"] #:result-label [result-label "converted"]) (define has-errors? (positive? (summary-ref summary 'errors 0))) (define should-send? - (and (manager-config-mail-enabled? config) - (not (null? (manager-config-mail-to config))) - (or (and has-errors? (manager-config-mail-send-on-error? config)) - (and (not has-errors?) (manager-config-mail-send-on-success? config))))) + (and (ini-ref/bool ini 'mail 'enabled #f) + (not (null? (ini-ref/addresses ini 'mail 'to ""))) + (or (and has-errors? (ini-ref/bool ini 'mail 'send-on-error #t)) + (and (not has-errors?) (ini-ref/bool ini 'mail 'send-on-success #f))))) (when should-send? - (define from (mail-address->envelope-address (manager-config-mail-from config))) - (define to (clean-address-list (manager-config-mail-to config))) - (define cc (clean-address-list (manager-config-mail-cc config))) - (define bcc (clean-address-list (manager-config-mail-bcc config))) + (define from (mail-address->envelope-address (ini-ref/string ini 'mail 'from ""))) + (define to (clean-address-list (ini-ref/addresses ini 'mail 'to ""))) + (define cc (clean-address-list (ini-ref/addresses ini 'mail 'cc ""))) + (define bcc (clean-address-list (ini-ref/addresses ini 'mail 'bcc ""))) (define subject (format "~a ~a: ~a error(s), ~a ~a" - (manager-config-mail-subject-prefix config) + (ini-ref/string ini 'mail 'subject-prefix "[flac-48khz-manager]") manager-name (summary-ref summary 'errors 0) (summary-ref summary 'converted 0) @@ -142,8 +142,8 @@ #:bcc bcc #:body-content-type "text/html")) (strict-send-smtp-mail mail - #:host (string-trim (manager-config-mail-host config)) - #:port (manager-config-mail-port config) - #:tls-encode (manager-config-mail-tls? config) - #:username (string-trim (manager-config-mail-username config)) - #:password (manager-config-mail-password config)))) + #:host (string-trim (ini-ref/string ini 'mail 'host "")) + #:port (ini-ref/int ini 'mail 'port 25) + #:tls-encode (ini-ref/bool ini 'mail 'tls #f) + #:username (string-trim (ini-ref/string ini 'mail 'username "")) + #:password (ini-ref/string ini 'mail 'password "")))) diff --git a/private/manager-ini.rkt b/private/manager-ini.rkt new file mode 100644 index 0000000..e5ec5fb --- /dev/null +++ b/private/manager-ini.rkt @@ -0,0 +1,77 @@ +#lang racket/base + +(require racket/file + racket/path + simple-ini + "util.rkt") + +(provide manager-ini-file + manager-state-file + ensure-default-manager-ini! + load-manager-ini + ini-ref/bool + ini-ref/int + ini-ref/string + ini-ref/path + ini-ref/addresses) + +(define (manager-ini-file base-dir*) + (build-path (filesystem-path base-dir*) ".flac-48khz-manager.ini")) + +(define (manager-state-file base-dir*) + (build-path (filesystem-path base-dir*) ".music-info.db")) + +(define (ensure-default-manager-ini! ini-file) + (unless (file-exists? ini-file) + (define ini (make-ini)) + (ini-set! ini 'manager 'max-sample-rate 48000) + (ini-set! ini 'manager 'hash-algorithm "sha256") + (ini-set! ini 'manager 'change-detection "flac-taglib") + (ini-set! ini 'manager 'dry-run #f) + (ini-set! ini 'manager 'display-log #t) + (ini-set! ini 'manager 'log-file ".flac-48khz-manager.log") + (ini-set! ini 'manager 'compression-level 5) + (ini-set! ini 'opus-manager 'log-file ".flac2opus-manager.log") + (ini-set! ini 'mail 'enabled #f) + (ini-set! ini 'mail 'send-on-success #f) + (ini-set! ini 'mail 'send-on-error #t) + (ini-set! ini 'mail 'host "") + (ini-set! ini 'mail 'port 25) + (ini-set! ini 'mail 'tls #f) + (ini-set! ini 'mail 'username "") + (ini-set! ini 'mail 'password "") + (ini-set! ini 'mail 'from "") + (ini-set! ini 'mail 'to "") + (ini-set! ini 'mail 'cc "") + (ini-set! ini 'mail 'bcc "") + (ini-set! ini 'mail 'subject-prefix "[flac-48khz-manager]") + (ini->file ini ini-file))) + +(define (load-manager-ini base-dir*) + (define ini-file (manager-ini-file base-dir*)) + (ensure-default-manager-ini! ini-file) + (file->ini ini-file)) + +(define (ini-ref/bool ini section key default) + (bool-value (ini-get ini section key default) default)) + +(define (ini-ref/int ini section key default) + (int-value (ini-get ini section key default) default)) + +(define (ini-ref/string ini section key default) + (string-value (ini-get ini section key default) default)) + +(define (resolve-ini-path base-dir v default) + (define p (string-value v default)) + (cond [(path-string? p) + (define bp (string->path p)) + (if (absolute-path? bp) bp (build-path base-dir bp))] + [else (build-path base-dir default)])) + +(define (ini-ref/path ini base-dir section key default) + (resolve-ini-path (filesystem-path base-dir) + (ini-get ini section key default) + default)) + +(define (ini-ref/addresses ini section key default) + (split-addresses (ini-get ini section key default))) diff --git a/scribblings/audio-library-manager.scrbl b/scribblings/audio-library-manager.scrbl index cfdfeef..f730cb1 100644 --- a/scribblings/audio-library-manager.scrbl +++ b/scribblings/audio-library-manager.scrbl @@ -70,8 +70,7 @@ implementation. @section{Configuration} -The configuration file is created automatically when it does not exist. The main -settings are: +The configuration file is created automatically when it does not exist. The managers read it directly as sections and keys through @racketmodname[simple-ini]; there is no separate configuration struct layered on top of the ini model. The main settings are: @verbatim{ [manager] @@ -83,6 +82,9 @@ display-log=#t log-file=".flac-48khz-manager.log" compression-level=5 +[opus-manager] +log-file=".flac2opus-manager.log" + [mail] enabled=#f send-on-success=#f