1 | open Logarion
|
---|
2 |
|
---|
3 | (*TODO: move to converters (style, feed checks)*)
|
---|
4 | let is_older s d = try Unix.((stat d).st_mtime < (stat s).st_mtime) with _-> true
|
---|
5 |
|
---|
6 | let convert cs r (text, files) = match Text.str "Content-Type" text with
|
---|
7 | | "" | "text/plain" ->
|
---|
8 | let source = List.hd files in
|
---|
9 | let dest = Filename.concat r.Conversion.dir (Text.short_id text) in
|
---|
10 | List.fold_left
|
---|
11 | (fun a f ->
|
---|
12 | match f.Conversion.page with None -> false || a
|
---|
13 | | Some page ->
|
---|
14 | let dest = dest ^ f.Conversion.ext in
|
---|
15 | (if is_older source dest then (File_store.file dest (page r text); true) else false)
|
---|
16 | || a)
|
---|
17 | false cs
|
---|
18 | | x -> Printf.eprintf "Can't convert Content-Type: %s file: %s" x text.Text.title; false
|
---|
19 |
|
---|
20 | let converters types kv =
|
---|
21 | let n = String.split_on_char ',' types in
|
---|
22 | let t = [] in
|
---|
23 | let t = if List.(mem "all" n || mem "htm" n) then (Html.converter kv)::t else t in
|
---|
24 | let t = if List.(mem "all" n || mem "atom" n) then (Atom.converter "text/html")::t else t in
|
---|
25 | let t = if List.(mem "all" n || mem "gmi" n) then (Gemini.converter)::t else t in
|
---|
26 | let t = if List.(mem "all" n || mem "gmi-atom" n) then (Atom.converter "text/gemini")::t else t in
|
---|
27 | t
|
---|
28 |
|
---|
29 | let acc_ref id t a =
|
---|
30 | Conversion.Id_map.update t (function
|
---|
31 | | Some s -> Some (Conversion.Ref_set.add id s)
|
---|
32 | | None -> Some (Conversion.Ref_set.singleton id)
|
---|
33 | ) a
|
---|
34 |
|
---|
35 | let fold_refs text refs = String_set.fold (acc_ref text.Text.id) (Text.set "references" text) refs
|
---|
36 | let fold_reps text reps = String_set.fold (acc_ref text.Text.id) (Text.set "in-reply-to" text) reps
|
---|
37 |
|
---|
38 | let directory converters noindex repo =
|
---|
39 | let fn (ts,refs,reps,ls,acc) ((elt,_) as r) =
|
---|
40 | Topic_set.to_map ts (Text.set "topics" elt),
|
---|
41 | fold_refs elt refs, fold_reps elt reps,
|
---|
42 | elt::ls,
|
---|
43 | if convert converters {repo with references = refs; replies = reps} r then acc+1 else acc in
|
---|
44 | let topics, references, replies, texts, count =
|
---|
45 | File_store.(fold ~dir:repo.Conversion.dir ~order:oldest fn
|
---|
46 | (Topic_set.Map.empty, Conversion.Id_map.empty, Conversion.Id_map.empty, [], 0)) in
|
---|
47 | let topic_roots = try List.rev @@ String_set.list_of_csv (Store.KV.find "Topics" repo.kv)
|
---|
48 | with Not_found -> Topic_set.roots topics in
|
---|
49 | Printf.eprintf "%d\n" (Conversion.Id_map.cardinal replies);
|
---|
50 | let repo = Conversion.{ repo with
|
---|
51 | topic_roots; topics; references; replies; texts = List.rev texts } in
|
---|
52 | if not noindex then
|
---|
53 | List.iter (fun c -> match c.Conversion.indices with None -> () | Some f -> f repo) converters;
|
---|
54 | Printf.printf "Converted: %d Indexed: %d\n" count (List.length texts)
|
---|
55 |
|
---|
56 | let load_kv dir =
|
---|
57 | let kv = File_store.of_kv_file () in
|
---|
58 | let idx = Filename.concat dir "index.pck" in
|
---|
59 | if not (Sys.file_exists idx) then kv else
|
---|
60 | match Header_pack.of_string @@ File_store.to_string (idx) with
|
---|
61 | | Error s -> prerr_endline s; kv
|
---|
62 | | Ok { info; peers; _ } ->
|
---|
63 | let kv = if Store.KV.mem "Id" kv then kv else Store.KV.add "Id" info.Header_pack.id kv in
|
---|
64 | let kv = if Store.KV.mem "Title" kv then kv else Store.KV.add "Title" info.Header_pack.title kv in
|
---|
65 | let kv = if Store.KV.mem "Locations" kv then kv else Store.KV.add "Locations" (String.concat ";\n" info.Header_pack.locations) kv in
|
---|
66 | let kv = Store.KV.add "Peers" (String.concat ";\n" Header_pack.(to_str_list peers)) kv in
|
---|
67 | kv
|
---|
68 |
|
---|
69 | let at_path types noindex path = match path with
|
---|
70 | | "" -> prerr_endline "unspecified text file or directory"
|
---|
71 | | path when Sys.file_exists path ->
|
---|
72 | if Sys.is_directory path then (
|
---|
73 | let kv = load_kv path in
|
---|
74 | let repo = { (Conversion.empty ()) with dir = path; kv } in
|
---|
75 | directory (converters types kv) noindex repo
|
---|
76 | ) else (
|
---|
77 | match File_store.to_text path with
|
---|
78 | | Error s -> prerr_endline s
|
---|
79 | | Ok text ->
|
---|
80 | let dir = "." in
|
---|
81 | let references, replies = File_store.(fold ~dir ~order:newest
|
---|
82 | (fun (refs, reps) (elt, _) -> fold_refs elt refs, fold_reps elt reps)
|
---|
83 | (Conversion.Id_map.empty, Conversion.Id_map.empty)) in
|
---|
84 | let repo = { (Conversion.empty ()) with dir; kv = load_kv ""; references; replies } in
|
---|
85 | ignore @@ convert (converters types repo.kv) repo (text, [path])
|
---|
86 | )
|
---|
87 | | path -> Printf.eprintf "Path doesn't exist: %s" path
|
---|
88 |
|
---|
89 | open Cmdliner
|
---|
90 | let term =
|
---|
91 | let path = Arg.(value & pos 0 string "" & info [] ~docv:"path"
|
---|
92 | ~doc:"Text file or directory to convert. If directory is provided, it must contain an index.pck (see: txt index)") in
|
---|
93 | let types = Arg.(value & opt string "all" & info ["t"; "type"] ~docv:"output type"
|
---|
94 | ~doc:"Convert to file type") in
|
---|
95 | let noindex = Arg.(value & flag & info ["noindex"]
|
---|
96 | ~doc:"Don't create indices in target format") in
|
---|
97 | Term.(const at_path $ types $ noindex $ path),
|
---|
98 | Term.info "convert" ~doc:"convert texts"
|
---|
99 | ~man:[ `S "DESCRIPTION"; `P "Convert text or indexed texts within a directory to another format.
|
---|
100 | If path is a directory must contain an index.pck. Run `txt index` first." ]
|
---|