added encryption, copy and replace.
This commit is contained in:
parent
ff62762484
commit
0b6eb07e82
20
README.md
20
README.md
|
@ -1,26 +1,38 @@
|
||||||
# image-index
|
# image-index
|
||||||
This project is a script which can index and sort files on your pc. You can give every file a title, category, source, tags and content for easier finding later on.
|
This project is a script which can index and sort files on your pc. You can give every file a title, category, source, tags and content for easier finding later on.
|
||||||
All information is stored inside of an SQLite-database. Two separate tables are used to give aliases to the categories and tags, so that a user can easily change the names without modifying all affected entries.
|
All information is stored inside of an SQLite-database. Two separate tables are used to give aliases to the categories and tags, so that a user can easily change the names without modifying all affected entries.
|
||||||
|
You can also encrypt all files with AES-CBC.
|
||||||
|
|
||||||
|
This project was written and tested on an Arch Linux-based distribution and python 3.10.
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
* add - Add a file and entry to the index
|
* add - Add a file and entry to the index
|
||||||
|
* copy - copies a file from the index to a custom location
|
||||||
* check - check if all files saved in the index exist and aren't faulty
|
* check - check if all files saved in the index exist and aren't faulty
|
||||||
* delete - delete a file and remove the entry
|
* delete - delete a file and remove the entry
|
||||||
* import - lets you add every file to the index in a chosen directory
|
* import - lets you add every file to the index in a chosen directory
|
||||||
* meta - a command to change aliases of categorties and tags
|
* meta - a command to change aliases of categories and tags
|
||||||
* open - open one or more files from the index in the default application (only Linux and Windows)
|
* open - open one or more files from the index in the default application (only Linux and Windows)
|
||||||
|
* replace - replaces a file in the index with another file while keeping the entry
|
||||||
* show - search through the index and show the matches
|
* show - search through the index and show the matches
|
||||||
* update - change a value of an entry in the index or move a file to another category
|
* update - change a value of an entry in the index or move a file to another category
|
||||||
|
|
||||||
## Installation
|
## Dependencies
|
||||||
|
* hashlib
|
||||||
|
* pycryptodomex
|
||||||
|
* sqlite3
|
||||||
|
|
||||||
|
## Downloading
|
||||||
|
There isn't an install script currently.
|
||||||
```sh
|
```sh
|
||||||
git clone https://gitlab.com/rodin_schule/image-index-py.git
|
git clone https://gitlab.com/rodin_schule/image-index-py.git
|
||||||
cd ./image-index-py
|
cd ./image-index-py
|
||||||
chmod u+x image-index
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
The top of the file holds some very important variables that need to be looked at by the user:
|
The top of the file holds some very important variables that need to be looked at by the user:
|
||||||
* ROOT_DIR: The absolute path of where you want to save your files (the directories for the categories will be created there)
|
* ROOT_DIR: The absolute path of where you want to save your files (the directories for the categories will be created there)
|
||||||
* CONFIG_DIR: The absolute path of where you want to store your `index.db`-file.
|
* INDEX_FILE: The absolute path of the database file.
|
||||||
* LINUX_APP_STARTER: The linux command which can open a file in the default application. Most distributions use `xdg-open`.
|
* LINUX_APP_STARTER: The linux command which can open a file in the default application. Most distributions use `xdg-open`.
|
||||||
|
* ENCRYPT: This setting tells the script whether to encrypt the added files by default or not.
|
||||||
|
* If set to True, the ids of the categories will be fully random (fe627ea4-3fd60 instead of category-3fd60) for pretty much zero-knowledge storage on a remote server without access to the database.
|
389
image-index
389
image-index
|
@ -1,15 +1,17 @@
|
||||||
#!/bin/python3
|
#!/usr/bin/python3
|
||||||
import hashlib,os,random,re,shutil,subprocess,sys
|
import ast,hashlib,os,random,re,secrets,shutil,subprocess,sys,tempfile
|
||||||
from pathlib import Path
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
import sqlite3 as sql
|
import sqlite3 as sql
|
||||||
|
from base64 import b64encode,b64decode
|
||||||
|
from Cryptodome.Cipher import AES
|
||||||
|
|
||||||
ROOT_DIR=os.getcwd() # The directory where all directories and files of the index are located
|
ROOT_DIR=os.getcwd() # The directory where all directories and files of the index are located
|
||||||
CONFIG_DIR=ROOT_DIR # The directory where the file 'index.db' is located
|
INDEX_FILE=ROOT_DIR+"/index.db" # The database file
|
||||||
LINUX_APP_STARTER="xdg-open" # The command which opens the files in the default applications
|
LINUX_APP_STARTER="xdg-open" # The command which opens the files in the default application
|
||||||
|
ENCRYPT=True # True or False; Whether the default is to encrypt the file or to save it as a plain file
|
||||||
|
|
||||||
class database():
|
class database():
|
||||||
def __init__(self,filepath = CONFIG_DIR + "/index.db"):
|
def __init__(self,filepath = INDEX_FILE):
|
||||||
self.connection=None
|
self.connection=None
|
||||||
self.crsr=None
|
self.crsr=None
|
||||||
if not os.path.exists(filepath) :
|
if not os.path.exists(filepath) :
|
||||||
|
@ -18,10 +20,11 @@ class database():
|
||||||
self.connection = sql.connect(filepath)
|
self.connection = sql.connect(filepath)
|
||||||
self.crsr = self.connection.cursor()
|
self.crsr = self.connection.cursor()
|
||||||
|
|
||||||
def add_index(self,vallist):
|
def add_index(self,vallist,collist=""):
|
||||||
|
collist=self.collist if not collist else collist
|
||||||
# compile the options into a command for the SQLite database
|
# compile the options into a command for the SQLite database
|
||||||
colstring=",".join(self.collist)
|
colstring=",".join(collist)
|
||||||
valstring="'{}'".format("','".join(vallist))
|
valstring='"{}"'.format('","'.join(vallist))
|
||||||
self.crsr.execute("""INSERT INTO {table} ({cols})
|
self.crsr.execute("""INSERT INTO {table} ({cols})
|
||||||
VALUES ({vals});
|
VALUES ({vals});
|
||||||
""".format(table=self.name,cols=colstring,vals=valstring))
|
""".format(table=self.name,cols=colstring,vals=valstring))
|
||||||
|
@ -51,11 +54,17 @@ class database():
|
||||||
ALIAS TEXT
|
ALIAS TEXT
|
||||||
); """
|
); """
|
||||||
self.crsr.execute(sqlcommand)
|
self.crsr.execute(sqlcommand)
|
||||||
|
sqlcommand = """CREATE TABLE ENCRYPTION(
|
||||||
|
NAME TEXT PRIMARY KEY NOT NULL,
|
||||||
|
CIPHER TEXT NOT NULL,
|
||||||
|
PASSWORD TEXT
|
||||||
|
); """
|
||||||
|
self.crsr.execute(sqlcommand)
|
||||||
|
|
||||||
def delete_index(self,typ,item):
|
def delete_index(self,typ,item):
|
||||||
self.crsr.execute("DELETE FROM {} WHERE {}='{}'".format(self.name,typ,item))
|
self.crsr.execute("DELETE FROM {} WHERE {}='{}'".format(self.name,typ,item))
|
||||||
self.connection.commit()
|
self.connection.commit()
|
||||||
return
|
return True
|
||||||
|
|
||||||
def get_col(self,column = "*"):
|
def get_col(self,column = "*"):
|
||||||
# get the column of some table. If no options given, return all columns
|
# get the column of some table. If no options given, return all columns
|
||||||
|
@ -64,9 +73,9 @@ class database():
|
||||||
res=[]
|
res=[]
|
||||||
for i in tres:
|
for i in tres:
|
||||||
res.append(i)
|
res.append(i)
|
||||||
# if the table is empty, return ".".
|
# if the table is empty, return "".
|
||||||
if not res:
|
if not res:
|
||||||
res="."
|
res=""
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def get_item(self,column,where,specific=False):
|
def get_item(self,column,where,specific=False):
|
||||||
|
@ -100,9 +109,9 @@ class database():
|
||||||
res.append(i)
|
res.append(i)
|
||||||
m+=1
|
m+=1
|
||||||
n+=1
|
n+=1
|
||||||
# if the table is empty, return ".".
|
# if the table is empty, return "".
|
||||||
if not res:
|
if not res:
|
||||||
return ["."]
|
return [""]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def select_index(self,sel_list,quiet=False):
|
def select_index(self,sel_list,quiet=False):
|
||||||
|
@ -121,7 +130,7 @@ class database():
|
||||||
if self.name == "FILES":
|
if self.name == "FILES":
|
||||||
print("\tTitle:\t ",temp_list[2])
|
print("\tTitle:\t ",temp_list[2])
|
||||||
name=ctb.get_alias(temp_list[4])
|
name=ctb.get_alias(temp_list[4])
|
||||||
if name != ".":
|
if name != "":
|
||||||
category=name
|
category=name
|
||||||
else:
|
else:
|
||||||
category=temp_list[4]
|
category=temp_list[4]
|
||||||
|
@ -129,7 +138,7 @@ class database():
|
||||||
tags_list=[]
|
tags_list=[]
|
||||||
for tag in temp_list[5].split(","):
|
for tag in temp_list[5].split(","):
|
||||||
name=ttb.get_alias(tag)
|
name=ttb.get_alias(tag)
|
||||||
if name != ".":
|
if name != "":
|
||||||
tag=name
|
tag=name
|
||||||
tags_list.append(tag)
|
tags_list.append(tag)
|
||||||
print("\tTags:\t ",",".join(tags_list))
|
print("\tTags:\t ",",".join(tags_list))
|
||||||
|
@ -140,7 +149,7 @@ class database():
|
||||||
n+=1
|
n+=1
|
||||||
eingabe=input("Enter number(s) (0-{}; '*' for all entries): ".format(n-1))
|
eingabe=input("Enter number(s) (0-{}; '*' for all entries): ".format(n-1))
|
||||||
if not eingabe:
|
if not eingabe:
|
||||||
return ["."]
|
return [""]
|
||||||
num_list=[]
|
num_list=[]
|
||||||
if re.match('[*]',eingabe):
|
if re.match('[*]',eingabe):
|
||||||
for i in range(0,n):
|
for i in range(0,n):
|
||||||
|
@ -157,10 +166,10 @@ class database():
|
||||||
if not quiet:
|
if not quiet:
|
||||||
print("\nFinal match{}:".format("es" if len(num_list)-nminus > 1 else ""))
|
print("\nFinal match{}:".format("es" if len(num_list)-nminus > 1 else ""))
|
||||||
else:
|
else:
|
||||||
if sel_list[0] == ".":
|
if sel_list[0] == "":
|
||||||
if not quiet == "strict":
|
if not quiet == "strict":
|
||||||
print("No matching entry found!")
|
print("No matching entry found!")
|
||||||
return ["."]
|
return [""]
|
||||||
res=sel_list
|
res=sel_list
|
||||||
if not quiet == "strict":
|
if not quiet == "strict":
|
||||||
print("\nMatch found!")
|
print("\nMatch found!")
|
||||||
|
@ -177,9 +186,9 @@ class database():
|
||||||
n=0
|
n=0
|
||||||
for i in self.get_col(typ):
|
for i in self.get_col(typ):
|
||||||
aliases=[]
|
aliases=[]
|
||||||
if i == "." and self.name == "FILES":
|
if i == "" and self.name == "FILES":
|
||||||
print("NO ENTRIES IN THE INDEX!")
|
print("NO ENTRIES IN THE INDEX!")
|
||||||
return "."
|
return ""
|
||||||
# get aliases for the checks, but only for unspecific search!
|
# get aliases for the checks, but only for unspecific search!
|
||||||
if self.name == "FILES":
|
if self.name == "FILES":
|
||||||
if typ == "*":
|
if typ == "*":
|
||||||
|
@ -220,7 +229,7 @@ class database():
|
||||||
n+=1
|
n+=1
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not secondlist[0] == ".":
|
if not secondlist[0] == "":
|
||||||
for i in secondlist:
|
for i in secondlist:
|
||||||
for j in firstlist:
|
for j in firstlist:
|
||||||
if j in i:
|
if j in i:
|
||||||
|
@ -229,7 +238,7 @@ class database():
|
||||||
else:
|
else:
|
||||||
return secondlist
|
return secondlist
|
||||||
if not temp_list:
|
if not temp_list:
|
||||||
return ["."]
|
return [""]
|
||||||
return temp_list
|
return temp_list
|
||||||
return secondlist
|
return secondlist
|
||||||
|
|
||||||
|
@ -238,8 +247,108 @@ class database():
|
||||||
self.connection.commit()
|
self.connection.commit()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
class enctable(database): # https://www.thesecuritybuddy.com/cryptography-and-python/aes-encryption-and-decryption-using-pycryptodome-module-in-python/
|
||||||
|
def __init__(self, filepath = INDEX_FILE):
|
||||||
|
self.name="ENCRYPTION"
|
||||||
|
self.collist=["NAME","CIPHER","PASSWORD"]
|
||||||
|
super().__init__(filepath)
|
||||||
|
|
||||||
|
def derive_key_and_iv(self, password, salt, key_length, iv_length): #derive key and IV from password and salt.
|
||||||
|
d = d_i = b''
|
||||||
|
while len(d) < key_length + iv_length:
|
||||||
|
#d_i = hashlib.md5(d_i + str.encode(password) + salt).digest() #obtain the md5 hash value
|
||||||
|
d_i = hashlib.md5(d_i + password + salt).digest() #obtain the md5 hash value
|
||||||
|
d += d_i
|
||||||
|
return d[:key_length], d[key_length:key_length+iv_length]
|
||||||
|
|
||||||
|
def encrypt(self, in_file, out_filepath, password="", key_length=32):
|
||||||
|
print("Encrypting...")
|
||||||
|
in_file=open(in_file,"rb")
|
||||||
|
out_uuid=out_filepath.split("/")[-1].split(".")[0]
|
||||||
|
out_file=open(f"{out_filepath}","wb")
|
||||||
|
bs = AES.block_size #16 bytes
|
||||||
|
if not password:
|
||||||
|
password = os.urandom(bs*random.randint(1,4))
|
||||||
|
if self.get_item("NAME", out_uuid)[0] == "":
|
||||||
|
self.add_index([out_uuid.split(".")[0],"AES",b64encode(password).decode()])
|
||||||
|
else:
|
||||||
|
password_list=self.get_item("NAME", in_uuid.split(".")[0])
|
||||||
|
if password_list[0] != "":
|
||||||
|
if len(password_list) == 1:
|
||||||
|
password=b64decode(password_list[0][2].encode())
|
||||||
|
else:
|
||||||
|
print("ERROR: MULTIPLE PASSWORD ENTRIES FOUND!")
|
||||||
|
return False
|
||||||
|
#print("ERROR: ENTRY FOR UUID {} ALREADY EXISTS!".format(out_uuid))
|
||||||
|
#return False
|
||||||
|
salt = os.urandom(bs) #return a string of random bytes
|
||||||
|
key, iv = self.derive_key_and_iv(password, salt, key_length, bs)
|
||||||
|
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||||
|
out_file.write(salt)
|
||||||
|
finished = False
|
||||||
|
|
||||||
|
while not finished:
|
||||||
|
chunk = in_file.read(1024 * bs)
|
||||||
|
if len(chunk) == 0 or len(chunk) % bs != 0:#final block/chunk is padded before encryption
|
||||||
|
padding_length = (bs - len(chunk) % bs) or bs
|
||||||
|
chunk += str.encode(padding_length * chr(padding_length))
|
||||||
|
finished = True
|
||||||
|
out_file.write(cipher.encrypt(chunk))
|
||||||
|
out_file.close()
|
||||||
|
in_file.close()
|
||||||
|
|
||||||
|
def is_encrypted(self, uuid):
|
||||||
|
if self.get_item("NAME", uuid)[0] != "":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def decrypt(self, in_filepath, out_file=None, password="", key_length=32):
|
||||||
|
print("Decrypting...")
|
||||||
|
in_file=open(f"{in_filepath}","rb") # open the encrypted file
|
||||||
|
in_uuid=in_filepath.split("/")[-1]
|
||||||
|
if not out_file:
|
||||||
|
out_temp=tempfile.mkstemp(prefix="image-index-")
|
||||||
|
filepath=out_temp[1]
|
||||||
|
out_file=open(filepath,"wb")
|
||||||
|
#out_file=temp_file
|
||||||
|
else:
|
||||||
|
filepath=out_file
|
||||||
|
out_file=open(out_file,"wb")
|
||||||
|
if not password:
|
||||||
|
password_list=self.get_item("NAME", in_uuid.split(".")[0])
|
||||||
|
if password_list[0] != "":
|
||||||
|
if len(password_list) == 1:
|
||||||
|
password=b64decode(password_list[0][2].encode())
|
||||||
|
else:
|
||||||
|
print("ERROR: MULTIPLE PASSWORD ENTRIES FOUND!")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
print("ERROR: NO PASSWORD FOUND FOR DECRYPTION!")
|
||||||
|
return False
|
||||||
|
bs = AES.block_size
|
||||||
|
salt = in_file.read(bs)
|
||||||
|
key, iv = self.derive_key_and_iv(password, salt, key_length, bs)
|
||||||
|
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||||
|
next_chunk = ''
|
||||||
|
finished = False
|
||||||
|
while not finished:
|
||||||
|
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
|
||||||
|
if len(next_chunk) == 0:
|
||||||
|
padding_length = chunk[-1]
|
||||||
|
chunk = chunk[:-padding_length]
|
||||||
|
finished = True
|
||||||
|
out_file.write(bytes(x for x in chunk))
|
||||||
|
out_file.close()
|
||||||
|
in_file.close()
|
||||||
|
return filepath
|
||||||
|
|
||||||
|
def delete_index(self, uuid):
|
||||||
|
super().delete_index("NAME", uuid)
|
||||||
|
print("UUID",uuid)
|
||||||
|
return True
|
||||||
|
|
||||||
class metatable(database):
|
class metatable(database):
|
||||||
def __init__(self,typ,filepath = CONFIG_DIR + "/index.db"):
|
def __init__(self,typ,filepath = INDEX_FILE):
|
||||||
self.name=typ.upper()
|
self.name=typ.upper()
|
||||||
self.collist=["NAME","ALIAS"]
|
self.collist=["NAME","ALIAS"]
|
||||||
super().__init__(filepath)
|
super().__init__(filepath)
|
||||||
|
@ -247,6 +356,9 @@ class metatable(database):
|
||||||
def add_index(self,val,alias,randhex=None):
|
def add_index(self,val,alias,randhex=None):
|
||||||
if not randhex:
|
if not randhex:
|
||||||
randhex=get_randhex()
|
randhex=get_randhex()
|
||||||
|
if self.name == "TAGS" or bencrypt:
|
||||||
|
val=get_randhex(8)
|
||||||
|
else:
|
||||||
val=re.sub('[ ,?!/\\:!*"<>|]', '', val)
|
val=re.sub('[ ,?!/\\:!*"<>|]', '', val)
|
||||||
super().add_index([val[:8] + "-" + randhex,alias])
|
super().add_index([val[:8] + "-" + randhex,alias])
|
||||||
|
|
||||||
|
@ -255,7 +367,7 @@ class metatable(database):
|
||||||
for i in tb.get_col(typ):
|
for i in tb.get_col(typ):
|
||||||
success=0
|
success=0
|
||||||
for j in self.get_col("NAME"):
|
for j in self.get_col("NAME"):
|
||||||
if j[0] in i[0]:
|
if j[0].split(".")[0] in i[0]:
|
||||||
success=1
|
success=1
|
||||||
if not i[0] in res and success == 0:
|
if not i[0] in res and success == 0:
|
||||||
res.append(i[0])
|
res.append(i[0])
|
||||||
|
@ -263,38 +375,41 @@ class metatable(database):
|
||||||
|
|
||||||
def get_alias(self,arg):
|
def get_alias(self,arg):
|
||||||
if not arg:
|
if not arg:
|
||||||
return "."
|
return ""
|
||||||
selection=self.search_index(arg,"strict")
|
selection=self.search_index(arg,"strict")
|
||||||
item=selection[0]
|
item=selection[0]
|
||||||
if item[0] != ".":
|
if item[0] != "":
|
||||||
alias=item[1]
|
alias=item[1]
|
||||||
else:
|
else:
|
||||||
alias = "."
|
alias = ""
|
||||||
return alias
|
return alias
|
||||||
|
|
||||||
def get_name(self,arg):
|
def get_name(self,arg):
|
||||||
if not arg:
|
if not arg:
|
||||||
return "."
|
return ""
|
||||||
selection=self.search_index(arg,"strict")
|
selection=self.search_index(arg,"strict")
|
||||||
item=selection[0]
|
item=selection[0]
|
||||||
|
if not item:
|
||||||
|
name=""
|
||||||
|
else:
|
||||||
name=item[0]
|
name=item[0]
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def search_index(self,args,quiet=True):
|
def search_index(self,args,quiet=True):
|
||||||
selection=[]
|
selection=[]
|
||||||
selection=self.sql_compare_list("*", [args], selection,True)
|
selection=self.sql_compare_list("*", [args], selection,True)
|
||||||
if selection[0] == ".":
|
if selection[0] == "":
|
||||||
slist=args.split(" ")
|
slist=args.split(" ")
|
||||||
selection=self.sql_compare_list("*", slist, [],False)
|
selection=self.sql_compare_list("*", slist, [],False)
|
||||||
if len(selection) > 1:
|
if len(selection) > 1:
|
||||||
print("Please enter a more specific search query!")
|
print("Please enter a more specific search query!")
|
||||||
return "."
|
return ""
|
||||||
selection=self.select_index(selection,quiet)
|
selection=self.select_index(selection,quiet)
|
||||||
return selection
|
return selection
|
||||||
|
|
||||||
def update_index(self, typ, update, where, val):
|
def update_index(self, typ, update, where, val):
|
||||||
selection=self.search_index(update,"strict")
|
selection=self.search_index(update,"strict")
|
||||||
if selection[0] != "." and len(selection) >= 1:
|
if selection[0] != "" and len(selection) >= 1:
|
||||||
print("One entry is already called {}!".format(update))
|
print("One entry is already called {}!".format(update))
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
@ -302,7 +417,7 @@ class metatable(database):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class filestable(database):
|
class filestable(database):
|
||||||
def __init__(self,filepath = CONFIG_DIR + "/index.db"):
|
def __init__(self,filepath = INDEX_FILE):
|
||||||
self.name="FILES"
|
self.name="FILES"
|
||||||
self.collist=["FILE","HASH","TITLE","SOURCE","CATEGORY","TAGS","CONTENT"]
|
self.collist=["FILE","HASH","TITLE","SOURCE","CATEGORY","TAGS","CONTENT"]
|
||||||
super().__init__(filepath)
|
super().__init__(filepath)
|
||||||
|
@ -310,14 +425,14 @@ class filestable(database):
|
||||||
def add_index(self,filepath,category="default",title="",source="",tags="",content=""):
|
def add_index(self,filepath,category="default",title="",source="",tags="",content=""):
|
||||||
filehash=self.get_hash(filepath) # make hash of file before copy
|
filehash=self.get_hash(filepath) # make hash of file before copy
|
||||||
if filehash in str(self.get_col("HASH")):
|
if filehash in str(self.get_col("HASH")):
|
||||||
print("This file already exists!")
|
print("This file already has an entry!")
|
||||||
return False
|
return False
|
||||||
n=0
|
n=0
|
||||||
# get the name of the category from the meta table
|
# get the name of the category from the meta table
|
||||||
if not category:
|
if not category:
|
||||||
category="default"
|
category="default"
|
||||||
name=ctb.get_name(category)
|
name=ctb.get_name(category)
|
||||||
if name != ".":
|
if name:
|
||||||
category=name
|
category=name
|
||||||
else:
|
else:
|
||||||
ctb.add_index(category.lower(),category)
|
ctb.add_index(category.lower(),category)
|
||||||
|
@ -327,20 +442,24 @@ class filestable(database):
|
||||||
tags_list=[]
|
tags_list=[]
|
||||||
for tag in tags.split(','):
|
for tag in tags.split(','):
|
||||||
name=ttb.get_name(tag)
|
name=ttb.get_name(tag)
|
||||||
if name != ".":
|
if name != "":
|
||||||
tag=name
|
tag=name
|
||||||
else:
|
else:
|
||||||
ttb.add_index(tag.lower(),tag)
|
ttb.add_index(tag.lower(),tag)
|
||||||
tags_list.append(ttb.get_name(tag))
|
tags_list.append(ttb.get_name(tag))
|
||||||
tags=",".join(tags_list)
|
tags=",".join(tags_list)
|
||||||
|
|
||||||
filetype=os.path.splitext(filepath)[1]
|
fileext=os.path.splitext(filepath)[-1]
|
||||||
filename=str(uuid4()) + filetype
|
fileuuid=str(uuid4())
|
||||||
|
filename=fileuuid+fileext
|
||||||
if not os.path.exists("{}/{}".format(ROOT_DIR,category)):
|
if not os.path.exists("{}/{}".format(ROOT_DIR,category)):
|
||||||
os.makedirs("{}/{}".format(ROOT_DIR,category))
|
os.makedirs("{}/{}".format(ROOT_DIR,category))
|
||||||
# try to copy the file, return if error.
|
# try to copy the file, return if error.
|
||||||
try:
|
try:
|
||||||
shutil.copy(filepath,"{}/{}/{}".format(ROOT_DIR,category,filename))
|
if bencrypt:
|
||||||
|
etb.encrypt(filepath, f"{ROOT_DIR}/{category}/{fileuuid}.enc")
|
||||||
|
else:
|
||||||
|
shutil.copy(filepath,f"{ROOT_DIR}/{category}/{filename}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
print("COULDN'T COPY FILE TO DESTINATION!")
|
print("COULDN'T COPY FILE TO DESTINATION!")
|
||||||
|
@ -352,45 +471,82 @@ class filestable(database):
|
||||||
def check_index(self):
|
def check_index(self):
|
||||||
hash_list=[]
|
hash_list=[]
|
||||||
path_list=[]
|
path_list=[]
|
||||||
|
enc_num=0
|
||||||
for i in self.get_col():
|
for i in self.get_col():
|
||||||
filename=i[0]
|
filename=i[0]
|
||||||
|
fileuuid=filename.split(".")[0]
|
||||||
category=i[4]
|
category=i[4]
|
||||||
filehash1=i[1]
|
filehash1=i[1]
|
||||||
|
if etb.is_encrypted(fileuuid):
|
||||||
|
enc_num+=1
|
||||||
|
filename=f"{fileuuid}.enc"
|
||||||
filepath="{}/{}/{}".format(ROOT_DIR,category,filename)
|
filepath="{}/{}/{}".format(ROOT_DIR,category,filename)
|
||||||
if not os.path.exists(filepath):
|
if not os.path.exists(filepath):
|
||||||
path_list.append(i)
|
path_list.append(i)
|
||||||
|
if etb.is_encrypted(fileuuid):
|
||||||
|
continue
|
||||||
try:
|
try:
|
||||||
filehash2=self.get_hash(filepath)
|
filehash2=self.get_hash(filepath)
|
||||||
if not filehash1 == filehash2:
|
if not filehash1 == filehash2:
|
||||||
hash_list.append(i)
|
hash_list.append(i)
|
||||||
except Exception:
|
except Exception:
|
||||||
path_list.append(i)
|
path_list.append(i)
|
||||||
|
print(f"Encrypted files: {enc_num}; hashes not checked.")
|
||||||
return hash_list,path_list
|
return hash_list,path_list
|
||||||
|
|
||||||
def delete_index(self,sel_list):
|
def delete_index(self,sel_list):
|
||||||
self.get_item("HASH",sel_list[1])
|
item_list=self.get_item(self.collist[1],sel_list[1])
|
||||||
super().delete_index("HASH",sel_list[1])
|
if len(item_list) == 1:
|
||||||
|
item=item_list[0]
|
||||||
|
category=item[4]
|
||||||
|
filename=item[0]
|
||||||
|
fileuuid=os.path.splitext(filename)[0]
|
||||||
|
super().delete_index(self.collist[1],sel_list[1])
|
||||||
|
etb.delete_index(fileuuid)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def update_index(self,typ,update,sel_list):
|
def replace_file(self,in_filepath,item):
|
||||||
|
filehash=self.get_hash(in_filepath)
|
||||||
|
category=item[4]
|
||||||
|
filename=item[0]
|
||||||
|
if filehash in str(self.get_col("HASH")):
|
||||||
|
print("This file already has an entry!")
|
||||||
|
return False
|
||||||
|
fileuuid=os.path.splitext(filename)[-1]
|
||||||
|
out_filepath=f"{ROOT_DIR}/{category}/{filename}"
|
||||||
|
out_encpath=f"{ROOT_DIR}/{category}/{fileuuid}.enc"
|
||||||
|
try:
|
||||||
|
if etb.is_encrypted(fileuuid):
|
||||||
|
etb.encrypt(in_filepath, out_encpath)
|
||||||
|
else:
|
||||||
|
shutil.copy(in_filepath, out_filepath)
|
||||||
|
except Exception as e:
|
||||||
|
print("ERROR:",e)
|
||||||
|
return False
|
||||||
|
super().update_index("HASH", filehash, self.collist[0], filename)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def update_index(self,typ,update,sel_list,omnipotent=False):
|
||||||
typ=typ.upper()
|
typ=typ.upper()
|
||||||
if typ in ["FILE","HASH"]:
|
if typ in ["FILE","HASH"] and not omnipotent:
|
||||||
print("This type can't be changed!")
|
print("This type can't be changed!")
|
||||||
return 1
|
return False
|
||||||
category=sel_list[4]
|
category=sel_list[4]
|
||||||
filehash=sel_list[1]
|
filehash=sel_list[1]
|
||||||
filename=sel_list[0]
|
filename=sel_list[0]
|
||||||
|
fileuuid=sel_list[0].split(".")[0]
|
||||||
if typ in ["CATEGORY"]:
|
if typ in ["CATEGORY"]:
|
||||||
# get alias of category
|
# get alias of category
|
||||||
name=ctb.get_name(update)
|
name=ctb.get_name(update)
|
||||||
if name != ".":
|
if name != "":
|
||||||
update=name
|
update=name
|
||||||
else:
|
else:
|
||||||
ctb.add_index(update.lower(),update)
|
ctb.add_index(update.lower(),update)
|
||||||
update=ctb.get_name(update)
|
update=ctb.get_name(update)
|
||||||
if not os.path.exists("{}/{}".format(ROOT_DIR,update)):
|
if not os.path.exists("{}/{}".format(ROOT_DIR,update)):
|
||||||
os.makedirs("{}/{}".format(ROOT_DIR,update))
|
os.makedirs("{}/{}".format(ROOT_DIR,update))
|
||||||
|
if etb.is_encrypted(fileuuid):
|
||||||
|
filename=f"{fileuuid}.enc"
|
||||||
shutil.move("{}/{}/{}".format(ROOT_DIR,category,filename), "{}/{}/{}".format(ROOT_DIR,update,filename))
|
shutil.move("{}/{}/{}".format(ROOT_DIR,category,filename), "{}/{}/{}".format(ROOT_DIR,update,filename))
|
||||||
if typ in ["TAGS"]:
|
if typ in ["TAGS"]:
|
||||||
tags_list=[]
|
tags_list=[]
|
||||||
|
@ -399,7 +555,7 @@ class filestable(database):
|
||||||
tags_list.append(tag)
|
tags_list.append(tag)
|
||||||
for tag in update[1:].split(","):
|
for tag in update[1:].split(","):
|
||||||
name=ttb.get_name(tag)
|
name=ttb.get_name(tag)
|
||||||
if name == ".":
|
if name == "":
|
||||||
ttb.add_index(tag.lower(), tag)
|
ttb.add_index(tag.lower(), tag)
|
||||||
name=ttb.get_name(tag)
|
name=ttb.get_name(tag)
|
||||||
tags_list.append(name)
|
tags_list.append(name)
|
||||||
|
@ -408,7 +564,7 @@ class filestable(database):
|
||||||
success=0
|
success=0
|
||||||
for i in update[1:].split(","):
|
for i in update[1:].split(","):
|
||||||
name=ttb.get_name(i)
|
name=ttb.get_name(i)
|
||||||
if name == ".":
|
if name == "":
|
||||||
ttb.add_index(tag.lower(), tag)
|
ttb.add_index(tag.lower(), tag)
|
||||||
name=ttb.get_name(tag)
|
name=ttb.get_name(tag)
|
||||||
if name == tag:
|
if name == tag:
|
||||||
|
@ -418,7 +574,7 @@ class filestable(database):
|
||||||
else:
|
else:
|
||||||
for tag in update.split(","):
|
for tag in update.split(","):
|
||||||
name=ttb.get_name(tag)
|
name=ttb.get_name(tag)
|
||||||
if name != ".":
|
if name != "":
|
||||||
tags_list.append(name)
|
tags_list.append(name)
|
||||||
else:
|
else:
|
||||||
ttb.add_index(tag.lower(), tag)
|
ttb.add_index(tag.lower(), tag)
|
||||||
|
@ -474,7 +630,7 @@ class filestable(database):
|
||||||
alle.append(arg)
|
alle.append(arg)
|
||||||
elif snext == "category":
|
elif snext == "category":
|
||||||
name=ctb.get_name(arg)
|
name=ctb.get_name(arg)
|
||||||
if name != ".":
|
if name != "":
|
||||||
arg=name
|
arg=name
|
||||||
category.append(arg)
|
category.append(arg)
|
||||||
elif snext == "file":
|
elif snext == "file":
|
||||||
|
@ -489,13 +645,13 @@ class filestable(database):
|
||||||
'''if "," in arg:
|
'''if "," in arg:
|
||||||
for tag in arg.split(","):
|
for tag in arg.split(","):
|
||||||
name=ttb.get_name(arg)
|
name=ttb.get_name(arg)
|
||||||
if name != ".":
|
if name != "":
|
||||||
arg=name
|
arg=name
|
||||||
tags.append(arg)
|
tags.append(arg)
|
||||||
else:
|
else:
|
||||||
name=ttb.get_name(arg)
|
name=ttb.get_name(arg)
|
||||||
print("name",name)
|
print("name",name)
|
||||||
if name != ".":
|
if name != "":
|
||||||
arg=name'''
|
arg=name'''
|
||||||
tags.append(arg)
|
tags.append(arg)
|
||||||
else:
|
else:
|
||||||
|
@ -525,14 +681,14 @@ def add(args):
|
||||||
return
|
return
|
||||||
n=0
|
n=0
|
||||||
for i in ["Filepath","Category","Title","Source","Tags","Content"]:
|
for i in ["Filepath","Category","Title","Source","Tags","Content"]:
|
||||||
|
i=i.title()
|
||||||
try:
|
try:
|
||||||
print("{}: {}".format(i,args[n]))
|
print("{}: {}".format(i,args[n]))
|
||||||
except Exception:
|
except Exception:
|
||||||
|
extra=""
|
||||||
if i == "Tags":
|
if i == "Tags":
|
||||||
extra=" (Separate with ',')"
|
extra=" (Separate with ',')"
|
||||||
else:
|
eingabe = input("{}{}: ".format(i,extra))
|
||||||
extra=""
|
|
||||||
eingabe = input("Enter {}{}: ".format(i,extra))
|
|
||||||
if i in ["Category"] and not eingabe:
|
if i in ["Category"] and not eingabe:
|
||||||
print("{} set to 'default'".format(i))
|
print("{} set to 'default'".format(i))
|
||||||
eingabe="default"
|
eingabe="default"
|
||||||
|
@ -546,7 +702,7 @@ def add(args):
|
||||||
print("{} must not be empty!".format(i))
|
print("{} must not be empty!".format(i))
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if not Path(args[n]).is_file():
|
if not os.path.isfile(args[n]):
|
||||||
print(" The file '{}' doesn't exist or is not a file!".format(args[n]))
|
print(" The file '{}' doesn't exist or is not a file!".format(args[n]))
|
||||||
return False
|
return False
|
||||||
n+=1
|
n+=1
|
||||||
|
@ -554,6 +710,36 @@ def add(args):
|
||||||
if success:
|
if success:
|
||||||
print("Added '{}'!".format(args[2]))
|
print("Added '{}'!".format(args[2]))
|
||||||
|
|
||||||
|
def copy(args):
|
||||||
|
if len(args) == 0:
|
||||||
|
out_filepath=input()
|
||||||
|
sel_list=search([])
|
||||||
|
elif len(args) == 1:
|
||||||
|
out_filepath=args[0]
|
||||||
|
sel_list=search([])
|
||||||
|
else:
|
||||||
|
out_filepath=args[0]
|
||||||
|
sel_list=search(args[1:])
|
||||||
|
for item in sel_list:
|
||||||
|
filename=item[0]
|
||||||
|
out_fileext=os.path.splitext(filename)[-1]
|
||||||
|
title=item[2]
|
||||||
|
category=item[4]
|
||||||
|
fileuuid=os.path.splitext(filename)[0]
|
||||||
|
extra=""
|
||||||
|
if os.path.exists(out_filepath):
|
||||||
|
if os.path.isfile(out_filepath):
|
||||||
|
if re.match('[nN].*', input(f"The file on path {out_filepath} already exists!\nDo you want to overwrite it? [Y/n] ")):
|
||||||
|
return False
|
||||||
|
if os.path.isdir(out_filepath):
|
||||||
|
print("found DIRECTORY")
|
||||||
|
extra=f"/{title}{out_fileext}"
|
||||||
|
if etb.is_encrypted(fileuuid):
|
||||||
|
etb.decrypt(f"{ROOT_DIR}/{category}/{fileuuid}.enc",f"{out_filepath}{extra}")
|
||||||
|
else:
|
||||||
|
shutil.copy(f"{ROOT_DIR}/{category}/{filename}", f"{out_filepath}{extra}")
|
||||||
|
print(f"Copied '{title}' to {out_filepath}!")
|
||||||
|
|
||||||
def check(args):
|
def check(args):
|
||||||
success=0
|
success=0
|
||||||
hash_list,temp_list=tb.check_index()
|
hash_list,temp_list=tb.check_index()
|
||||||
|
@ -603,10 +789,13 @@ def check(args):
|
||||||
def delete(args):
|
def delete(args):
|
||||||
selection=search(args,True)
|
selection=search(args,True)
|
||||||
for sel in selection:
|
for sel in selection:
|
||||||
if sel[0] != ".":
|
if sel[0] != "":
|
||||||
try:
|
try:
|
||||||
category=sel[4]
|
category=sel[4]
|
||||||
filename=sel[0]
|
filename=sel[0]
|
||||||
|
fileuuid=os.path.splitext(filename)[0]
|
||||||
|
if etb.is_encrypted(fileuuid):
|
||||||
|
filename=f"{fileuuid}.enc"
|
||||||
os.remove("{}/{}/{}".format(ROOT_DIR,category,filename))
|
os.remove("{}/{}/{}".format(ROOT_DIR,category,filename))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
@ -624,11 +813,17 @@ def help(args):
|
||||||
if re.match('[aA].*',arg):
|
if re.match('[aA].*',arg):
|
||||||
print("add:\tadds a new entry;\n\tInstant: image-index add <filepath> <category> <title> <source> <tags> <content>")
|
print("add:\tadds a new entry;\n\tInstant: image-index add <filepath> <category> <title> <source> <tags> <content>")
|
||||||
print("\tPrompt: image-index add")
|
print("\tPrompt: image-index add")
|
||||||
|
print("Encryption: -e: Encrypt the added file\n\t -p: Do not encrypt the added file (plain)")
|
||||||
print('EXAMPLES:\nimage-index add ~/Pictures/example.jpg "A Category" "Example file" "https://example.org" "Tag,Example,Some thing" "This is an example for the add option."')
|
print('EXAMPLES:\nimage-index add ~/Pictures/example.jpg "A Category" "Example file" "https://example.org" "Tag,Example,Some thing" "This is an example for the add option."')
|
||||||
print("image-index add ~/Videos/movie.mp4 (This will ask for the other options in a prompt)\n")
|
print("image-index add ~/Videos/movie.mp4 (This will ask for the other options in a prompt)\n")
|
||||||
elif re.match('[mM].*',arg):
|
elif re.match('[mM].*',arg):
|
||||||
meta_help()
|
meta_help()
|
||||||
elif re.match('[cC].*', arg):
|
elif re.match('[cC].*', arg):
|
||||||
|
print("copy:\tcopies a file to a custom filepath based on a search query;\n\tInstant: image-index copy <filepath> <words/filters>")
|
||||||
|
print("\tPrompt: image-index copy")
|
||||||
|
print("EXAMPLE:\nimage-index copy ~/Pictures/Example.jpg -t example")
|
||||||
|
print("image-index copy ~/Pictures/ -t example (creates a file with the title and extension of the entry)\n")
|
||||||
|
elif re.match('[cC][hH].*',arg) or re.match('[cC].*[eE].*',arg):
|
||||||
print("check:\tchecks the existence and correctness of all files in the index;\n\tSyntax: image-index check [options]")
|
print("check:\tchecks the existence and correctness of all files in the index;\n\tSyntax: image-index check [options]")
|
||||||
print("\tOptions: -v: show every faulty/orphaned entry")
|
print("\tOptions: -v: show every faulty/orphaned entry")
|
||||||
print("\t\t -f: check only if files exist (disables the other check)")
|
print("\t\t -f: check only if files exist (disables the other check)")
|
||||||
|
@ -644,6 +839,10 @@ def help(args):
|
||||||
print("open:\topens a file based on a search query in the standard app;\n\tInstant: image-index open <words/filters>")
|
print("open:\topens a file based on a search query in the standard app;\n\tInstant: image-index open <words/filters>")
|
||||||
print("\tPrompt: image-index open")
|
print("\tPrompt: image-index open")
|
||||||
print("EXAMPLE:\nimage-index open example -s example.org -i an example\n")
|
print("EXAMPLE:\nimage-index open example -s example.org -i an example\n")
|
||||||
|
elif re.match('[rR].*',arg):
|
||||||
|
print("replaces a file of an entry based on a search query;\n\tInstant: image-index replace <replacement_file> <words/filters>")
|
||||||
|
print("\tPrompt: image-index replace")
|
||||||
|
print("EXAMPLE:\nimage-index replace ~/Pictures/example_new.jpg -f .jpg\n")
|
||||||
elif re.match('[sS].*',arg):
|
elif re.match('[sS].*',arg):
|
||||||
print("show:\tsearches through the index and shows the matches;\n\tInstant: image-index show <words/filters>")
|
print("show:\tsearches through the index and shows the matches;\n\tInstant: image-index show <words/filters>")
|
||||||
print("\tPrompt: image-index show")
|
print("\tPrompt: image-index show")
|
||||||
|
@ -664,9 +863,11 @@ def help(args):
|
||||||
print("\tmeta:\tdisplays help for the metadata tables")
|
print("\tmeta:\tdisplays help for the metadata tables")
|
||||||
print("\tadd:\tadds a new entry;\n\t\tSyntax: image-index add <filepath> <category> <title> <source> <tags> <content>")
|
print("\tadd:\tadds a new entry;\n\t\tSyntax: image-index add <filepath> <category> <title> <source> <tags> <content>")
|
||||||
print("\tcheck:\tchecks the existence and correctness of all files in the index;\n\t\tSyntax: image-index check [options]")
|
print("\tcheck:\tchecks the existence and correctness of all files in the index;\n\t\tSyntax: image-index check [options]")
|
||||||
|
print("\tcopy:\tcopies a file to a custom filepath based on a search query;\n\t\tSyntax: image-index copy <filepath> <words/filters>")
|
||||||
print("\tdelete:\tdeletes a file and entry based on a search query;\n\t\tSyntax: image-index delete <words/filters>")
|
print("\tdelete:\tdeletes a file and entry based on a search query;\n\t\tSyntax: image-index delete <words/filters>")
|
||||||
print("\timport:\tshows an add prompt for every file in a directory;\n\t\tSyntax: image-index import <directory path>")
|
print("\timport:\tshows an add prompt for every file in a directory;\n\t\tSyntax: image-index import <directory path>")
|
||||||
print("\topen:\topens a file based on a search query in the standard app;\n\t\tSyntax: image-index open <words/filters>")
|
print("\topen:\topens a file based on a search query in the standard app;\n\t\tSyntax: image-index open <words/filters>")
|
||||||
|
print("\treplace:replaces a file of an entry based on a search query;\n\t\tSyntax: image-index replace <file> <words/filters>")
|
||||||
print("\tshow:\tsearches through the index and shows the matches;\n\t\tSyntax: image-index show <words/filters>")
|
print("\tshow:\tsearches through the index and shows the matches;\n\t\tSyntax: image-index show <words/filters>")
|
||||||
print("\tupdate:\tchanges specific column based on a search query;\n\t\tSyntax: image-index update <column> <updated_value> <words/filters>")
|
print("\tupdate:\tchanges specific column based on a search query;\n\t\tSyntax: image-index update <column> <updated_value> <words/filters>")
|
||||||
|
|
||||||
|
@ -675,10 +876,9 @@ def imports(args):
|
||||||
if arg[-1] != "/":
|
if arg[-1] != "/":
|
||||||
arg+="/"
|
arg+="/"
|
||||||
if os.path.exists(arg):
|
if os.path.exists(arg):
|
||||||
if not Path(arg).is_file():
|
if not os.path.isfile(arg):
|
||||||
for sfile in os.listdir(arg):
|
for sfile in os.listdir(arg):
|
||||||
if Path(arg,sfile).is_file():
|
if os.path.isfile(arg+sfile):
|
||||||
#print("{}{}".format(arg,sfile))
|
|
||||||
add(["{}{}".format(arg,sfile)])
|
add(["{}{}".format(arg,sfile)])
|
||||||
else:
|
else:
|
||||||
print("Path '{}' is a file!".format(arg))
|
print("Path '{}' is a file!".format(arg))
|
||||||
|
@ -747,8 +947,8 @@ def meta_update(typ,args):
|
||||||
success=ttb.update_index("ALIAS", update, "NAME", item[0])
|
success=ttb.update_index("ALIAS", update, "NAME", item[0])
|
||||||
else:
|
else:
|
||||||
print("The first argument needs to be either 'Category' or 'Tags'!")
|
print("The first argument needs to be either 'Category' or 'Tags'!")
|
||||||
return 1
|
return False
|
||||||
if item[0] != "." and success:
|
if item[0] != "" and success:
|
||||||
print("Updated {} to {}".format(alias,update))
|
print("Updated {} to {}".format(alias,update))
|
||||||
|
|
||||||
def meta_help():
|
def meta_help():
|
||||||
|
@ -763,12 +963,18 @@ def opens(args):
|
||||||
plat=sys.platform
|
plat=sys.platform
|
||||||
selection=search(args,True)
|
selection=search(args,True)
|
||||||
for sel in selection:
|
for sel in selection:
|
||||||
if not sel[0] == ".":
|
if not sel[0] == "":
|
||||||
|
fileuuid=os.path.splitext(sel[0])[0]
|
||||||
|
if etb.is_encrypted(fileuuid):
|
||||||
|
filename=f"{fileuuid}.enc"
|
||||||
|
else:
|
||||||
filename=sel[0]
|
filename=sel[0]
|
||||||
category=sel[4]
|
category=sel[4]
|
||||||
filepath="{}/{}/{}".format(ROOT_DIR,category,filename)
|
filepath="{}/{}/{}".format(ROOT_DIR,category,filename)
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
if etb.is_encrypted(fileuuid):
|
||||||
|
filepath=etb.decrypt(filepath) # temporary file in RAM
|
||||||
if plat.startswith('linux'):
|
if plat.startswith('linux'):
|
||||||
subprocess.Popen([LINUX_APP_STARTER, filepath])
|
subprocess.Popen([LINUX_APP_STARTER, filepath])
|
||||||
else:
|
else:
|
||||||
|
@ -787,7 +993,7 @@ def update(args):
|
||||||
typ=args[0]
|
typ=args[0]
|
||||||
update=args[1]
|
update=args[1]
|
||||||
for sel in selection:
|
for sel in selection:
|
||||||
if sel[0] != ".":
|
if sel[0] != "":
|
||||||
n=0
|
n=0
|
||||||
for i in tb.collist:
|
for i in tb.collist:
|
||||||
if i.lower() == typ.lower():
|
if i.lower() == typ.lower():
|
||||||
|
@ -820,6 +1026,30 @@ def repair(err_list):
|
||||||
os.remove(filepath)
|
os.remove(filepath)
|
||||||
tb.delete_index(tup)
|
tb.delete_index(tup)
|
||||||
|
|
||||||
|
def replace(args):
|
||||||
|
if len(args) == 0:
|
||||||
|
filepath=input("Enter filepath of the replacement: ")
|
||||||
|
sel_list=search([])
|
||||||
|
elif len(args) == 1:
|
||||||
|
filepath=args[0]
|
||||||
|
sel_list=search([])
|
||||||
|
else:
|
||||||
|
filepath=args[0]
|
||||||
|
sel_list=search(args[1:])
|
||||||
|
if len (sel_list) != 1:
|
||||||
|
print("Please select one entry for replacement!")
|
||||||
|
return False
|
||||||
|
item=sel_list[0]
|
||||||
|
if os.path.exists(filepath) and os.path.isfile(filepath):
|
||||||
|
title=item[2]
|
||||||
|
if tb.replace_file(filepath, item):
|
||||||
|
print(f"Replaced '{title}'!")
|
||||||
|
else:
|
||||||
|
print(f"Failed to replace '{title}'!")
|
||||||
|
else:
|
||||||
|
print("ERROR: Replacement doesn't exist or is not a file!")
|
||||||
|
return False
|
||||||
|
|
||||||
def search(args,quiet=False):
|
def search(args,quiet=False):
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
print("Separate the items with spaces.")
|
print("Separate the items with spaces.")
|
||||||
|
@ -830,28 +1060,28 @@ def search(args,quiet=False):
|
||||||
res=tb.search_index(args.split(' '),quiet)
|
res=tb.search_index(args.split(' '),quiet)
|
||||||
else:
|
else:
|
||||||
print("\nQuery empty!")
|
print("\nQuery empty!")
|
||||||
return ["."]
|
return [""]
|
||||||
else:
|
else:
|
||||||
res=tb.search_index(args,quiet)
|
res=tb.search_index(args,quiet)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def show(args):
|
def show(args):
|
||||||
tres=search(args,False)
|
tres=search(args,False)
|
||||||
if not tres[0] == ".":
|
if not tres[0] == "":
|
||||||
for res in tres:
|
for res in tres:
|
||||||
print("Title: ",res[2])
|
print("Title: ",res[2])
|
||||||
print("\tSource:\t ",res[3])
|
print("\tSource:\t ",res[3])
|
||||||
alias=ctb.get_alias(res[4])
|
alias=ctb.get_alias(res[4])
|
||||||
if alias != ".":
|
if alias != "":
|
||||||
print("\tCategory: {} ({})".format(alias,res[4]))
|
print("\tCategory: {} ({})".format(alias,res[4]))
|
||||||
else:
|
else:
|
||||||
print("\tCategory:",res[4])
|
print("\tCategory:",res[4])
|
||||||
print("\tFilename:",res[0])
|
print("\tFilename:",res[0],"(Encrypted)" if etb.is_encrypted(os.path.splitext(res[0])[0]) else "")
|
||||||
print("\tHash:\t ",res[1])
|
print("\tHash:\t ",res[1])
|
||||||
tags_list=[]
|
tags_list=[]
|
||||||
for tag in res[5].split(","):
|
for tag in res[5].split(","):
|
||||||
alias=ttb.get_alias(tag)
|
alias=ttb.get_alias(tag)
|
||||||
if alias != ".":
|
if alias != "":
|
||||||
tags_list.append(alias)
|
tags_list.append(alias)
|
||||||
print("\tTags:\t ",",".join(tags_list))
|
print("\tTags:\t ",",".join(tags_list))
|
||||||
print("\tContent: ",res[6])
|
print("\tContent: ",res[6])
|
||||||
|
@ -862,20 +1092,30 @@ def main():
|
||||||
else:
|
else:
|
||||||
command=sys.argv[1]
|
command=sys.argv[1]
|
||||||
args=[]
|
args=[]
|
||||||
for i in sys.argv[2:]:
|
for arg in sys.argv[2:]:
|
||||||
args.append(i)
|
global bencrypt
|
||||||
|
if re.match("[-]{1,2}e.*",arg): # -e --encrypted
|
||||||
|
bencrypt=True
|
||||||
|
if re.match("[-]{1,2}p.*",arg): # -p --plain
|
||||||
|
bencrypt=False
|
||||||
|
else:
|
||||||
|
args.append(arg)
|
||||||
if re.match('[aA].*',command):
|
if re.match('[aA].*',command):
|
||||||
add(args)
|
add(args)
|
||||||
elif re.match('[mM].*',command):
|
elif re.match('[mM].*',command):
|
||||||
meta(args)
|
meta(args)
|
||||||
elif re.match('[cC].*',command):
|
elif re.match('[cC][hH].*',command) or re.match('[cC].*[eE].*',command):
|
||||||
check(args)
|
check(args)
|
||||||
|
elif re.match('[cC].*',command):
|
||||||
|
copy(args)
|
||||||
elif re.match('[dD].*',command):
|
elif re.match('[dD].*',command):
|
||||||
delete(args)
|
delete(args)
|
||||||
elif re.match('[iI].*',command):
|
elif re.match('[iI].*',command):
|
||||||
imports(args)
|
imports(args)
|
||||||
elif re.match('[oO].*',command):
|
elif re.match('[oO].*',command):
|
||||||
opens(args)
|
opens(args)
|
||||||
|
elif re.match('[rR].*',command):
|
||||||
|
replace(args)
|
||||||
elif re.match('[sS].*',command):
|
elif re.match('[sS].*',command):
|
||||||
show(args)
|
show(args)
|
||||||
elif re.match('[uU].*',command):
|
elif re.match('[uU].*',command):
|
||||||
|
@ -884,11 +1124,14 @@ def main():
|
||||||
print("No such option!")
|
print("No such option!")
|
||||||
tb.connection.close()
|
tb.connection.close()
|
||||||
ctb.connection.close()
|
ctb.connection.close()
|
||||||
|
etb.connection.close()
|
||||||
ttb.connection.close()
|
ttb.connection.close()
|
||||||
#input("Press return...")
|
#input("Press return...")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
filepath=CONFIG_DIR + '/index.db'
|
bencrypt=ENCRYPT # boolean for encryption in session
|
||||||
|
filepath=INDEX_FILE
|
||||||
|
etb = enctable(filepath)
|
||||||
tb = filestable(filepath)
|
tb = filestable(filepath)
|
||||||
ctb = metatable("CATEGORY",filepath)
|
ctb = metatable("CATEGORY",filepath)
|
||||||
ttb = metatable("TAGS",filepath)
|
ttb = metatable("TAGS",filepath)
|
||||||
|
|
Loading…
Reference in a new issue