.
675 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # ann2html
2 | # See LICENSE file for copyright and license details.
3 |
4 | .PHONY: dist linux-x86_64 windows-x86_64 macos-x86_64 macos-arm64 clean-releases
5 |
6 | GOOPTIONS = CGO_ENABLED=0
7 | LDFLAGS = -ldflags="-w -s -buildid=" -trimpath -o
8 |
9 | all: clean-releases dist
10 |
11 | dist: linux-x86_64 windows-x86_64 macos-x86_64 macos-arm64
12 |
13 | linux-x86_64:
14 | ${GOOPTIONS} GOOS=linux GOARCH=amd64 go build ${LDFLAGS} release/$@/ann2html/ann2html
15 | cp release-includes/README-linux release/$@/ann2html/README
16 | cp release-includes/ann release/$@/ann2html/ann
17 | cp release-includes/config release/$@/ann2html/config
18 | cp release-includes/template.html release/$@/ann2html/template.html
19 | tar --owner=0 --group=0 --mode='og-w' -czvf release/ann2html-$@.tar.gz -C release/$@ .
20 | rm -r release/$@
21 |
22 |
23 | windows-x86_64:
24 | ${GOOPTIONS} GOOS=windows GOARCH=amd64 go build ${LDFLAGS} release/$@/ann2html/ann2html.exe
25 | cp release-includes/README-windows release/$@/ann2html/README.txt
26 | cp release-includes/config release/$@/ann2html/config
27 | cp release-includes/template.html release/$@/ann2html/template.html
28 | (cd release/$@ && zip -9 -y -r -X - ann2html/ > ../ann2html-$@.zip)
29 | rm -r release/$@
30 |
31 |
32 | macos-x86_64:
33 | ${GOOPTIONS} GOOS=darwin GOARCH=amd64 go build ${LDFLAGS} release/$@/ann2html/ann2html
34 | cp release-includes/README-macos release/$@/ann2html/README
35 | cp release-includes/config release/$@/ann2html/config
36 | cp release-includes/template.html release/$@/ann2html/template.html
37 | (cd release/$@ && zip -9 -y -r -X - ann2html/ > ../ann2html-$@.zip)
38 | rm -r release/$@
39 |
40 | macos-arm64:
41 | ${GOOPTIONS} GOOS=darwin GOARCH=arm64 go build ${LDFLAGS} release/$@/ann2html/ann2html
42 | cp release-includes/README-macos release/$@/ann2html/README
43 | cp release-includes/config release/$@/ann2html/config
44 | cp release-includes/template.html release/$@/ann2html/template.html
45 | (cd release/$@ && zip -9 -y -r -X - ann2html/ > ../ann2html-$@.zip)
46 | rm -r release/$@
47 |
48 | clean-releases:
49 | rm -f release/*
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ann2html
2 | ## Description
3 | A small program that uses Kindle's vocabulary builder and turns it into a usable HTML page.
4 | 
5 |
6 |
7 | ## Features
8 | * Single executable, portable install.
9 | * Pressing b on the page will bookmark your current position.
10 | * The template file's css and javascript can be easily modified.
11 | * Automatically bolds selected word from Vocabulary builder(It will skip it words that vocabulary builder deconjugates and it can't match)
12 |
13 | ## Installation
14 | Download your operation system specific release,extract it and move the folder wherever you would like it, ann2html does not need to be installed in any specific location.
15 |
16 | If you'd like to build it from source, clone the repo and run `go build .`. There is also a makefile for making all of the releases.
17 |
18 | ## Usage
19 | Copy your vocab.db file from your kindle which should be located at
20 |
21 | | Operation System | vocab.db location |
22 | | ---------------- | ----------------- |
23 | | Windows | KINDLEDRIVELETTER:\system\vocabulary\vocab.db |
24 | | MacOS | MOUNTPOINT/system/vocabulary/vocab.db |
25 | | Linux | MOUNTPOINT/system/vocabulary/vocab.db |
26 |
27 | Where KINDLEDRIVELETTER is the drive letter that Windows assigns to your Kindle and MOUNTPOINT being the mount point that MacOS or Linux assigns to your Kindle.
28 |
29 | Copy this vocab.db into the folder with the ann2html executable and run ann2html. It should generate an edit.html file which has all your annotations. Everytime you run ann2html it will check to see if your vocab.db has changed from the last time you ran ann2html and if it has it will regenerate, showing only your new annotations.
30 |
31 |
32 | ## Configuration
33 | The program can be configured in two ways, by directly editing the configuration file or setting the environment variable, with environment variables taking precedence.
34 |
35 | ### Default configuration values
36 |
37 | | Option | default value | explanation |
38 | | --------------- | --------------- | --------------- |
39 | | ANN2HTML_CONFIG |NOTSET | env variable to change the location of your config file. |
40 | | ANN2HTML_VOCABDB | vocab.db | location of your vocab.db. |
41 | | ANN2HTML_NUM | 0 | unix timestamp of the last annotation, this is changed by the program.|
42 | | ANN2HTML_TEMPLATE | template.html | File to use as a template for your output file.|
43 | | ANN2HTML_OUTPUT | edit.html | where you want your annotation to be written, this file will be replaced on each run. |
44 | | ANN2HTML_LNG| ja | list of languages you want to get annotations from, split by , example:ja,en|
45 |
46 | ### Windows
47 | Windows hides the vocab.db file in a odd way, so if you are on windows go to your kindle's drive and then search for vocab.db, then copy vocab.db into the folder where the ann2html program is and it should work.
48 |
49 | ### Macos
50 | Macos does not allow running unsigned executables easily. To run this open up your terminal and `cd` into the folder with the ann2html executable. Then it can be ran running `./ann2html`
51 |
52 |
53 | ### Linux script
54 | For linux users there is an optional script called ann which allows easy mounting of your kindle. To use the script add your kindle's uuid to it,make it executableand add it to your path.This script does depend on udisksctl so make sure to install it.
55 |
56 | To find your uuid run ```ls -l /dev/disk/by-uuid``` and then connect your kindle and run it again.
57 | ```
58 | ls -l /dev/disk/by-uuid
59 | total 0
60 | lrwxrwxrwx 1 root root 10 Nov 10 00:00 3472-5482 -> ../../nvme
61 | lrwxrwxrwx 1 root root 10 Nov 10 00:00 3472-54821212 -> ../../nvme2
62 | PLUG IN KINDLE AND RUN AGAIN
63 | ls -l /dev/disk/by-uuid
64 | total 0
65 | lrwxrwxrwx 1 root root 10 Nov 10 00:00 3472-5482 -> ../../nvme
66 | lrwxrwxrwx 1 root root 10 Nov 10 00:00 3251-54821212 -> ../../nvme2
67 | lrwxrwxrwx 1 root root 10 Nov 10 00:00 3582-6578 -> ../../sda
68 | in this example the UUID you want is 3582-6578
69 | ```
70 | Then in the script called ann add your kindle uuid to the following line.
71 | ```
72 | kindle_UUID=
73 | ```
74 | ```
75 | kindle_UUID=3582-6578
76 | ```
77 | Then run chmod +x ann
78 | and then move ann anywhere in your path, now you can just run ann and your kindle will be mounted and ann2html will be ran!
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | change the template file to use go html templates.
2 | look something like this.
3 | {{ {start-of-sentence} {vocab-word}<\b> {end-of-sentence}
}}
4 | Update go.mod versions
5 |
6 | [DONE] looks for config in executable location, no longer needs to be
7 | "installed", test this.
8 | [DONE] check ANN2HTML_NUM and ANN2HTML_LANG, test this.
9 |
10 |
--------------------------------------------------------------------------------
/ann2html.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/joho/godotenv"
7 | "os"
8 | )
9 |
10 | func main() {
11 | configLocation, err := getConfig()
12 | ErrorCheck(err)
13 | // godotenv loads environment variables from a file but does
14 | //not replace them if they are in the environment
15 | err = godotenv.Load(configLocation)
16 | ErrorCheck(err)
17 | vocabDB := os.Getenv("ANN2HTML_VOCABDB")
18 | ErrorCheck(checkEnvVar(vocabDB, "ANN2HTML_VOCABDB"))
19 | lastNumber := os.Getenv("ANN2HTML_NUM")
20 | ErrorCheck(checkEnvVar(lastNumber, "ANN2HTML_NUM"))
21 | templateFile := os.Getenv("ANN2HTML_TEMPLATE")
22 | ErrorCheck(checkEnvVar(templateFile, "ANN2HTML_TEMPLATE"))
23 | outFile := os.Getenv("ANN2HTML_OUTPUT")
24 | ErrorCheck(checkEnvVar(outFile, "ANN2HTML_OUTPUT"))
25 | languages := os.Getenv("ANN2HTML_LNG")
26 | vocabMap, newNum, err := databaseToMap(vocabDB, languages, lastNumber)
27 | ErrorCheck(err)
28 | err = buildFile(vocabMap, templateFile, outFile)
29 | ErrorCheck(err)
30 | SetNum(newNum, configLocation)
31 | ErrorCheck(err)
32 |
33 | }
34 | func checkEnvVar(envVar, envName string) error {
35 | //Check if environmentable variable is set and if not return
36 | //error
37 | if envVar == "" {
38 | return errors.New(envName + " is not set")
39 | }
40 | return nil
41 | }
42 |
43 | func ErrorCheck(err error) {
44 | //check if function throws error and if it does display it and exit the program
45 | if err != nil {
46 | fmt.Println(err)
47 | os.Exit(1)
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/dbtomap.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "errors"
6 | "golang.org/x/text/language"
7 | _ "modernc.org/sqlite"
8 | "os"
9 | "strconv"
10 | "strings"
11 | )
12 |
13 | func databaseToMap(databaseFile string, lang string, lastTime string) ([]string, string, error) {
14 | //takes the path to your vocab.db,languages you want to mine from and the highest unix timestamp from the last time
15 | //the program was ran then returns a map with all your annotations, the highest unix timestamp found and an error.
16 | if fileExists(databaseFile) != true {
17 | return nil, "", errors.New("your vocab.db was not found")
18 | os.Exit(1)
19 | }
20 | db, err := sql.Open("sqlite", databaseFile)
21 | if err != nil {
22 | return nil, "", errors.New("error accesing vocab.db")
23 | }
24 | // make sure lastTime is a number
25 | _, err = strconv.ParseInt(lastTime, 10, 64)
26 | if err != nil {
27 | return nil, "", err
28 | }
29 |
30 | defer db.Close()
31 | languages, err := getLanguages(lang)
32 | if err != nil {
33 | return nil, "", err
34 | }
35 | rows, err := db.Query("SELECT timestamp,word_key,usage from LOOKUPS where timestamp > " + lastTime + languages)
36 | ErrorCheck(err)
37 | defer rows.Close()
38 |
39 | var final []string
40 | var count string
41 | defer rows.Close()
42 | for rows.Next() {
43 | var timestamp, word_key, usage string
44 | err = rows.Scan(×tamp, &word_key, &usage)
45 | if err != nil {
46 | return nil, "", errors.New("error forming map")
47 |
48 | }
49 | count = timestamp
50 | // this count is used to keep track of the last unix timestamp in your kindle database
51 | final = append(final, formatSent(word_key, usage))
52 | }
53 | if len(final) == 0 {
54 | return final, count, errors.New("No new annotations")
55 |
56 | }
57 | return final, count, nil
58 | }
59 |
60 | func formatSent(word_key, sentence string) string {
61 | //finds your mined words in your sentences and replace it
62 | //with word
63 | word := strings.Split(word_key, ":")
64 |
65 | replacement := "" + word[1] + ""
66 | newSentence := strings.Replace(sentence, word[1], replacement, 1)
67 | return newSentence
68 | }
69 | func getLanguages(annLanguages string) (string, error) {
70 | //splits your ANN2HTML_LNG string into a map with all the languages you want to mine from,
71 | //then it return a appropriate string to add to the database query it only queries from those
72 | //languages
73 | var andWord string
74 | var extra string
75 | if annLanguages == "" {
76 | return "", nil
77 | //if nothing is set than it mines from all languages.
78 | }
79 |
80 | languages := strings.Split(annLanguages, ",")
81 | // make sure our string of languages are valid languages
82 | for _, v := range languages {
83 | _, err := language.ParseBase(v)
84 | if err != nil {
85 | return "", errors.New(v + "is not a correct ISO 639 language code")
86 | }
87 | }
88 | andWord = " AND word_key LIKE '" + languages[0] + ":%'"
89 | if len(languages) == 1 {
90 | return andWord, nil
91 | }
92 |
93 | for _, value := range languages[1:] {
94 | extra = extra + " OR word_key LIKE '" + value + ":%'"
95 |
96 | }
97 | return andWord + extra, nil
98 | }
99 |
--------------------------------------------------------------------------------
/files.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "os"
7 | )
8 |
9 | func buildFile(databaseMap []string, template string, outFile string) error {
10 | //erase old file, copy template file to replace it and add new annotations
11 | //this is kinda scuffed, probably want to change how this is done, in a way
12 | // that the program is aware of html format.
13 | os.Remove(outFile)
14 | _, err := copyFile(template, outFile)
15 | if err != nil {
16 | return errors.New("Failure to copy template to output file")
17 | }
18 | f, err := os.OpenFile(outFile, os.O_APPEND|os.O_WRONLY, 0644)
19 | if err != nil {
20 | return errors.New("Failure to open newly created output file")
21 | }
22 | for _, k := range databaseMap {
23 | _, err = fmt.Fprintln(f, "
", k, "
")
24 | if err != nil {
25 | return errors.New("Failure to append to output file")
26 | }
27 | }
28 | fmt.Fprintln(f, "