logarion/cli/convert.ml
orbifx e4654aa652 Accept comma separated converter names, separate Atom from Html and Gemini converters.
Note: atom must be called separately now because of the separation. Example

txt convert -t htm,atom xyz
2022-10-26 20:36:02 +01:00

77 lines
3.8 KiB
OCaml

open Logarion
let is_older source dest = try
Unix.((stat dest).st_mtime < (stat source).st_mtime) with _-> true
let convert cs r (text, files) = match Text.str "Content-Type" text with
| "" | "text/plain" ->
let source = List.hd files in
let dest = Filename.concat r.Conversion.dir (Text.short_id text) in
List.fold_left
(fun a f ->
match f.Conversion.page with None -> false || a
| Some page ->
let dest = dest ^ f.Conversion.ext in
(if is_older source dest then (File_store.file dest (page r text); true) else false)
|| a)
false cs
| x -> Printf.eprintf "Can't convert Content-Type: %s file: %s" x text.Text.title; false
let converters types kv =
let n = String.split_on_char ',' types in
let t = [] in
let t = if List.(mem "all" n || mem "htm" n) then (Html.converter kv)::t else t in
let t = if List.(mem "all" n || mem "atom" n) then (Atom.converter "text/html")::t else t in
let t = if List.(mem "all" n || mem "gmi" n) then (Gemini.converter)::t else t in
let t = if List.(mem "all" n || mem "gmi-atom" n) then (Atom.converter "text/gemini")::t else t in
t
let directory converters noindex dir id kv =
let empty = Topic_set.Map.empty in
let repo = Conversion.{ id; dir; kv; topic_roots = []; topics = empty; texts = [] } in
let fn (ts,ls,acc) ((elt,_) as r) =
(Topic_set.to_map ts (Text.set "topics" elt)), elt::ls,
if convert converters repo r then acc+1 else acc in
let topics, texts, count = File_store.(fold ~dir ~order:newest fn (empty,[],0)) in
let topic_roots = try List.rev @@ String_set.list_of_csv (Store.KV.find "Topics" kv)
with Not_found -> Topic_set.roots topics in
let repo = Conversion.{ repo with topic_roots; topics; texts } in
if not noindex then List.iter (fun c -> match c.Conversion.indices with None -> () | Some f -> f repo) converters;
Printf.printf "Converted: %d Indexed: %d\n" count (List.length texts)
let at_path types noindex path =
match path with "" -> prerr_endline "unspecified text file or directory"
| dir when Sys.file_exists dir && Sys.is_directory dir ->
let fname = Filename.concat dir "index.pck" in
(match Header_pack.of_string @@ File_store.to_string fname with
| Error s -> prerr_endline s
| Ok { info; peers; _ } ->
let kv = let f = Filename.concat dir ".convert.conf" in (* TODO: better place to store convert conf? *)
if Sys.file_exists f then File_store.of_kv_file f else Store.KV.empty in
let kv = if Store.KV.mem "Title" kv then kv else Store.KV.add "Title" info.Header_pack.title kv in
let kv = Store.KV.add "Locations" (String.concat ";\n" info.Header_pack.locations) kv in
let kv = Store.KV.add "Peers" (String.concat ";\n" Header_pack.(to_str_list peers)) kv in
let cs = converters types kv in
directory cs noindex dir info.Header_pack.id kv)
| path when Sys.file_exists path ->
let repo = Conversion.{
id = ""; dir = ""; kv = Store.KV.empty; topic_roots = [];
topics = Topic_set.Map.empty; texts = [] } in
let cs = converters types repo.kv in
(match File_store.to_text path with
| Ok text -> ignore @@ convert cs repo (text, [path])
| Error s -> prerr_endline s)
| path -> Printf.eprintf "Path doesn't exist: %s" path
open Cmdliner
let term =
let path = Arg.(value & pos 0 string "" & info [] ~docv:"path"
~doc:"Text file or directory to convert. Ff directory is provided, it must contain an index.pck (see: txt index)") in
let types = Arg.(value & opt string "all" & info ["t"; "type"] ~docv:"output type"
~doc:"Convert to file type") in
let noindex = Arg.(value & flag & info ["noindex"]
~doc:"Don't create indices in target format") in
Term.(const at_path $ types $ noindex $ path),
Term.info "convert" ~doc:"convert texts"
~man:[ `S "DESCRIPTION"; `P "Convert text or indexed texts within a directory to another format.
If path is a directory must contain an index.pck. Run `txt index` first." ]