From 3bf01f286f6dfbecdbf636848d1ba495817f3f76 Mon Sep 17 00:00:00 2001 From: cjnaz Date: Fri, 6 Dec 2019 22:58:47 -0700 Subject: [PATCH] v0.3 --- LICENSE.txt | 21 +++++++++++++++++ README.md | 23 +++++++++++++++---- xbsjsonedit | 66 ++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 92 insertions(+), 18 deletions(-) create mode 100755 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100755 index 0000000..5bbb007 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Chris Nelson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index f7a5742..6302dca 100755 --- a/README.md +++ b/README.md @@ -9,16 +9,22 @@ If you want it to do other functions, grab the code and go for it. ## Usage ``` $ ./xbsjsonedit -h -usage: xbsjsonedit [-h] [--dump] Infile +usage: xbsjsonedit [-h] [--Print] [--Tags-Print] [--Tags-Count TAGS_COUNT] + [-V] + Infile xBrowserSync json backup editor positional arguments: - Infile json backup file + Infile json backup file optional arguments: - -h, --help show this help message and exit - --dump Dump bookmark hierarchy (redirect to less or a file) + -h, --help show this help message and exit + --Print, -p Print bookmark hierarchy (redirect to less or a file) + --Tags-Print, -t Print tags list + --Tags-Count TAGS_COUNT, -c TAGS_COUNT + Filter Tags-List for min number of times a tag is used (defult 2). =0 prints only single use tags. + -V, --version Return version number and exit. ``` ## Internal command menu @@ -61,7 +67,9 @@ Five modes are supported Listing/Deleting based on: - Folders that have tags - there is no value for folders having tags - Empty folders - which may arise if all bookmarks were deleted from a folder -The Print function prints the full list of bookmarks in folder hierarchical form, listing the ID number and Title of each folder and bookmark. The `--dump` switch may be used for getting a good visual dump of the bookmarks in less or an editor for reference while using the interactive mode. +The Print function prints the full list of bookmarks in folder hierarchical form, listing the ID number and Title of each folder and bookmark. The `--Print` switch may be used for getting a good visual dump of the bookmarks in less or an editor for reference while using the interactive mode. The tags on each bookmark are listed as well. + +The `--Tags-Print` switch prints bookmarks by tag name, where a minimum of 2 bookmarks share the same tag, as set by the `--Tags-Count` switch. Setting `--Tags-Count 1` prints all bookmarks with tags. Setting `--Tags-Count 0` prints all bookmarks with only one tag - useful for finding remnant tags. A lower case letter `t/g/d/f/x/y` will list the offenders, while an upper case letter `T/G/D/F/X/Y` will allow for selective deletes of the offenders. The lower case List operation @@ -88,3 +96,8 @@ syncID, so you may want to Disable Sync, then create a new sync before restoring - This code only works with Python 3. Development and testing was done on Linux with Python 3.7.3. +## Version history + +- 191206 v0.3 Added tags dump +- 191205 v0.2 Updated to xbrowsersync v1.5 and Python 3.x ONLY +- 181128 v0.1 New \ No newline at end of file diff --git a/xbsjsonedit b/xbsjsonedit index d2573a8..900d381 100755 --- a/xbsjsonedit +++ b/xbsjsonedit @@ -1,13 +1,16 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- +"""xBrowserSync json backup editor""" + +__version__ = "v0.3 191206" #========================================================== # -desc = """xBrowserSync json backup editor""" # Chris Nelson 2018 - 2019 # -# 191205 Updated to xbrowsersync v1.5 and Python 3.x ONLY -# 181128 New +# 191206 v0.3 Added tags dump +# 191205 v0.2 Updated to xbrowsersync v1.5 and Python 3.x ONLY +# 181128 v0.1 New # # Issues, and changes pending # None @@ -31,6 +34,7 @@ NONE_SEARCH = "__NONE__" # Global items url_dict = {} folder_dict = {} +tags_dict = {} def main(): @@ -40,15 +44,29 @@ def main(): global json_dict global url_dict global folder_dict + global tags_dict with io.open(args.Infile, encoding='utf8', errors="replace") as json_data: json_dict = json.load(json_data) term = NONE_SEARCH - if args.dump: + if args.Print: digin(parent=json_dict["xbrowsersync"]["data"]["bookmarks"], parent_id="", path="/", search_term="", operation="printtree") exit() + if args.Tags_Print: + digin(parent=json_dict["xbrowsersync"]["data"]["bookmarks"], parent_id="", path="/", search_term="", operation="GatherTags") + for tag in sorted(tags_dict.keys()): + if args.Tags_Count == 0: + if len(tags_dict[tag]) == 1: + print ("\n[{}]:".format(tag)) + print (" {:<4} - {}".format(tags_dict[tag][0]["id"], tags_dict[tag][0]["title"])) + else: + if len(tags_dict[tag]) >= args.Tags_Count: + print ("\n[{}]:".format(tag)) + for bookmark in tags_dict[tag]: + print (" {:<4} - {}".format(bookmark["id"], bookmark["title"])) + exit() while (1): match_cnt = 0 @@ -150,8 +168,7 @@ Options: if ans == "": ans = args.Infile + OFILESUFFIX with io.open(ans, "w", encoding='utf8') as ofile: - # json.dump(json_dict, ofile, indent=2) #, encoding="latin-1") - json.dump(json_dict, ofile, ensure_ascii=False, indent=2) #, encoding="latin-1") + json.dump(json_dict, ofile, ensure_ascii=False, indent=2) elif select == 'q': exit() @@ -267,7 +284,12 @@ def digin(parent, parent_id, path, search_term, operation, commit=False, indent= else: # ***** Its a Leaf node ***** if operation == "printtree": - print ("{:<4} - {}{}".format(item["id"], indent, item["title"])) + tags = "" + if "tags" in item: + + for tag in item["tags"]: + tags += tag + ", " + print ("{:<4} - {}{} [{}]".format(item["id"], indent, item["title"], tags[:-2])) if operation == "DupURLs": log_urls (item["url"], path, item["id"], parent, parent_id, item_index) @@ -311,6 +333,16 @@ def digin(parent, parent_id, path, search_term, operation, commit=False, indent= path_dict = {} return -1 + if operation == "GatherTags": + if "tags" in item: + if item["tags"] != None: + for tag in item["tags"]: + if tag not in tags_dict: + tags_dict[tag] = [{"title":item["title"], "id":item["id"]}] + else: + tags_dict[tag].append({"title":item["title"], "id":item["id"]}) + + path_dict = {} def collect_items (path, parent, index): @@ -418,9 +450,9 @@ def dup_urls (commit=False): def prompt (prompt_text): - if sys.version_info[0] < 3: - return (raw_input(prompt_text)) - else: + # if sys.version_info[0] < 3: + # return (raw_input(prompt_text)) + # else: return (input(prompt_text)) @@ -470,11 +502,19 @@ def prompt (prompt_text): if __name__ == '__main__': - parser = argparse.ArgumentParser(description=desc, formatter_class=argparse.RawTextHelpFormatter) + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('Infile', help="json backup file") - parser.add_argument('--dump', action='store_true', - help="Dump bookmark hierarchy (redirect to less or a file)") + parser.add_argument('--Print', '-p', action='store_true', + help="Print bookmark hierarchy (redirect to less or a file)") + parser.add_argument('--Tags-Print', '-t', action='store_true', + help="Print tags list") + parser.add_argument('--Tags-Count', '-c', default=2, type=int, + help="Filter Tags-List for min number of times a tag is used (defult 2). =0 prints only single use tags.") + parser.add_argument('-V', '--version', + help="Return version number and exit.", + action='version', + version='%(prog)s ' + __version__) args = parser.parse_args() if not os.path.exists(args.Infile):