Changeset 3 in code for trunk/cli/convert.ml


Ignore:
Timestamp:
Apr 15, 2022, 1:17:01 PM (3 years ago)
Author:
fox
Message:
  • Removed 'txt init'

Format

  • New B32 ID

Index

  • New option: txt index --print
  • Move scheme to peers
  • Replace peer.*.conf files with index packed locations Instead of adding a URL to peers.*.conf, run txt pull <url>

Conversion

  • Rewritten converters
  • txt-convert looks for a .convert.conf containing key: value lines.
  • Specifiable topic-roots from .convert.conf.
  • Added Topics: key, with comma seperated topics.

If set only those topics will appear in the main index and used as topic roots.
Other topics will have sub-indices generated, but won't be listed in the main index.

  • HTML converter header & footer options
  • HTML-index renamed to HTM-index

Internal

  • Change types: uuid:Uuid -> id:string
  • File_store merges identical texts
  • Use peer ID for store path, store peers' texts in .local/share/texts
  • Simple URN resolution for converter

Continue to next feed if parsing one fails

  • Phasing-out Archive, replaced by improved packs
  • Eliminate Bos, Cohttp, lwt, uri, tls, Re, Ptime, dependencies
  • Lock version for Cmdliner, fix dune-project
  • Optional resursive store
  • Improve header_pack
  • Fix recursive mkdir
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/cli/convert.ml

    r2 r3  
    11open Logarion
    2 module A = Archive.Make (Logarion.File_store)
    32
    4 let convert_modified source dest fn title text =
    5         if (try Unix.((stat dest).st_mtime < (stat source).st_mtime) with _-> true)
    6         then (File_store.file dest (fn title text); true) else false
     3let is_older source dest = try
     4        Unix.((stat dest).st_mtime < (stat source).st_mtime) with _-> true
    75
    8 let word_fname dir text = dir ^ "/" ^ Text.alias text
    9 let id_fname dir text = dir ^ "/" ^ Text.short_id text
     6let 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                                let dest = dest ^ f.Conversion.ext in
     13                                if is_older source dest then (File_store.file dest (f.Conversion.page r text); true) else false
     14                                || a)
     15                        false cs
     16        | x -> Printf.eprintf "Can't convert Content-Type: %s file: %s" x text.Text.title; false
    1017
    11 let writer types dir name (text,store_item) = (* todo: single_parser -> [files] *)
    12 (*      convert_modified store_item idfilename (fun _title -> Text.to_string) text.title text;*)
    13         let h = if "htm" = types || "all" = types then
    14                 convert_modified store_item (id_fname dir text ^ ".htm") Html.page name text
    15                 else false in
    16         let g = if "gmi" = types || "all" = types then
    17                 convert_modified store_item (id_fname dir text ^ ".gmi") Gemini.page name text
    18                 else false in
    19         h || g
     18let converters types kv =
     19        let t = [] in
     20        let t = if ("htm" = types || "all" = types) then
     21                (let htm = Html.init kv in
     22                 Conversion.{ ext = Html.ext; page = Html.page htm; indices = Html.indices htm })::t
     23                else t in
     24        let t = if ("gmi" = types || "all" = types) then
     25                Conversion.{ ext = Gemini.ext; page = Gemini.page; indices = Gemini.indices}::t else t in
     26        t
    2027
    21 let index_writer types noindex dir archive topic_roots topic_map texts =
    22         let name = archive.A.name in
    23         let file path = File_store.file (dir ^ path) in
    24         file "/index.pck"       (Header_pack.pack archive texts);
    25         if not noindex && ("htm" = types || "all" = types) then (
    26                 let index_name = try Store.KV.find "HTML-index" archive.File_store.kv
    27                         with Not_found -> "index.html" in
    28                 if index_name <> "" then
    29                         file ("/"^index_name) (Html.topic_main_index name topic_roots texts);
    30                 file "/index.date.htm" (Html.date_index name texts);
    31                 List.iter
    32                         (fun topic -> file ("/index." ^ topic ^ ".htm")
    33                          (Html.topic_sub_index name topic_map topic texts))
    34                         topic_roots;
    35                 let base_url = try Store.KV.find "HTTP-URL" archive.File_store.kv
    36                         with Not_found -> prerr_endline "Missing `HTTP-URL:` in config"; "" in
    37                 file "/feed.atom" (Atom.feed archive.A.name archive.A.id base_url "text/html" texts)
    38         );
    39         if not noindex && ("gmi" = types || "all" = types) then (
    40                 let index_name = try Store.KV.find "Gemini-index" archive.File_store.kv
    41                         with Not_found -> "index.gmi" in
    42                 if index_name <> "" then
    43                         file ("/"^index_name) (Gemini.topic_main_index name topic_roots texts);
    44                 file "/index.date.gmi" (Gemini.date_index name texts);
    45                 List.iter
    46                         (fun topic -> file ("/index." ^ topic ^ ".gmi")
    47                          (Gemini.topic_sub_index name topic_map topic texts))
    48                         topic_roots;
    49                 let base_url = try Store.KV.find "GEMINI-URL" archive.File_store.kv
    50                         with Not_found -> prerr_endline "Missing `GEMINI-URL:` in config"; "" in
    51                 file "/gmi.atom" (Atom.feed archive.A.name archive.A.id base_url "text/gemini" texts)
    52         )
     28let convert_all converters noindex dir id kv =
     29        let empty = Topic_set.Map.empty in
     30        let repo = Conversion.{ id; dir; kv; topic_roots = []; topics = empty; texts = [] } in
     31        let fn (ts,ls,acc) ((elt,_) as r) =
     32                (Topic_set.to_map ts (Text.set "topics" elt)), elt::ls,
     33                if convert converters repo r then acc+1 else acc in
     34        let topics, texts, count = File_store.(fold ~dir ~order:newest fn (empty,[],0)) in
     35        let topic_roots = try List.rev @@ String_set.list_of_csv (Store.KV.find "Topics" kv)
     36                with Not_found -> Topic_set.roots topics in
     37        let repo = Conversion.{ repo with topic_roots; topics; texts } in
     38        if not noindex then List.iter (fun c -> c.Conversion.indices repo) converters;
     39        Printf.printf "Converted: %d Indexed: %d\n" count (List.length texts)
    5340
    54 let txt_writer types dir name ((text, _store_item) as r) =
    55         match Text.str "Content-Type" text with
    56         | "" | "text/plain" -> writer types dir name r
    57         | x -> prerr_endline ("Can't convert Content-Type: "^x^" file: " ^text.Text.title); false
    58 
    59 let convert_all types noindex dir archive =
    60         let name = archive.A.name in
    61         let fn (ts,ls,acc) ((elt,_) as r) =
    62                 (Topic_set.to_map ts (Text.set "topics" elt)),
    63                 elt::ls, if txt_writer types dir name r then acc+1 else acc in
    64         let empty = Topic_set.Map.empty in
    65         let topic_map, texts, count = A.(fold ~order:newest fn (empty,[],0) archive) in
    66         let topic_roots = Topic_set.roots topic_map in
    67         index_writer types noindex dir archive topic_roots topic_map texts;
    68         print_endline @@ "Converted: " ^ string_of_int (count)
    69                 ^ "\nIndexed: " ^ string_of_int (List.length texts);
    70         Ok ()
    71 
    72 let convert_dir types noindex cmd_dir =
    73         let (>>=) = Result.bind in
    74         let with_dir dir =
    75                 Result.map_error (function `Msg m -> m)
    76                 Logarion.File_store.Directory.(directory dir |> print ~descr:"export" dir) in
    77         (A.of_path "."
    78                 >>= fun archive -> (match cmd_dir with "" -> Error "unspecified export dir" | x -> Ok x)
    79                 >>= fun dir -> with_dir dir
    80                 >>= fun _ -> convert_all types noindex dir { archive with store = dir })
    81          |> function Ok () -> () | Error x -> prerr_endline x
     41let convert_dir types noindex dir =
     42        match dir with "" -> prerr_endline "unspecified dir"
     43        | dir ->
     44                let fname = Filename.concat dir "index.pck" in
     45                match Header_pack.of_string @@ File_store.to_string fname with
     46                | Error s -> prerr_endline s
     47                | Ok { info; _ } ->
     48                        let kv = let f = Filename.concat dir ".convert.conf" in (* TODO: better place to store convert conf? *)
     49                                if Sys.file_exists f then File_store.of_kv_file f else Store.KV.empty in
     50                        let kv = if Store.KV.mem "Title" kv then kv
     51                                else Store.KV.add "Title" info.Header_pack.title kv in
     52                        let kv = Store.KV.add "Locations" (String.concat ";\n" info.Header_pack.locations) kv in
     53                        let cs = converters types kv in
     54                        convert_all cs noindex dir info.Header_pack.id kv
    8255
    8356open Cmdliner
    84 
    8557let term =
    86         let directory = Arg.(value & pos 0 string "" & info [] ~docv:"target directory" ~doc:"Directory to convert into") in
    87         let types = Arg.(value & opt string "all" & info ["t"; "type"] ~docv:"TYPES" ~doc:"Convert to type") in
    88         let noindex = Arg.(value & flag & info ["noindex"] ~doc:"don't write an index when converting") in
     58        let directory = Arg.(value & pos 0 string "" & info [] ~docv:"target directory"
     59                ~doc:"Directory to convert") in
     60        let types = Arg.(value & opt string "all" & info ["t"; "type"] ~docv:"TYPES"
     61         ~doc:"Convert to type") in
     62        let noindex = Arg.(value & flag & info ["noindex"]
     63                ~doc:"don't create indices in target format") in
    8964        Term.(const convert_dir $ types $ noindex $ directory),
    90         Term.info "convert" ~doc:"convert archive" ~man:[ `S "DESCRIPTION"; `P "Convert Logarion archive" ]
     65        Term.info "convert" ~doc:"convert txts"
     66                ~man:[ `S "DESCRIPTION"; `P "Convert texts within a directory to another format.
     67                Directory must contain an index.pck. Run `txt index` first." ]
Note: See TracChangeset for help on using the changeset viewer.