diff --git a/TODO b/TODO index db4e85f..a5edaed 100644 --- a/TODO +++ b/TODO @@ -1,12 +1,10 @@ ## GENERAL * viewer: - buttons need tootltips - can we make it preload next/prev images, and only reload the image div when we jump? to make arrow-based nav much faster - im_next = new Image() - im_next.src="..." - few things though: - - have to remember the details of not just the src fname, but also face* details - - onload event, what if we use it and use say arrow keys to load/change image, will one beat the other, but if we arrow next too fast, the img wont be loaded? + - buttons need tootltips to show shortcut key + - if DrawImg runs into a video, deal with it :) + + * files.py cleanup: + class Option for dot-notation, or namespace??? * remove dirs after the duplicate cleanup removes all its content diff --git a/files.py b/files.py index 36a6069..fc66f97 100644 --- a/files.py +++ b/files.py @@ -78,6 +78,7 @@ class Dir(db.Model): # {dir|file}_etails are convenience data for the relevant details from the Dir # or File class - not in DB # in_dir - is the Dir that this entry is located in (convenience for class only) +# FullPathOnFS(): method to get path on the FS for this Entry ################################################################################ class Entry(db.Model): __tablename__ = "entry" @@ -89,6 +90,17 @@ class Entry(db.Model): file_details = db.relationship( "File", uselist=False ) in_dir = db.relationship ("Dir", secondary="entry_dir_link", uselist=False ) + def FullPathOnFS(self): + if self.in_dir: + s=self.in_dir.in_path.path_prefix + '/' + if len(self.in_dir.rel_path) > 0: + s += self.in_dir.rel_path + '/' + # this occurs when we have a dir that is the root of a path + else: + s=self.dir_details.in_path.path_prefix+'/' + s += self.name + return s + def __repr__(self): return f" helper function that Gets Entries for required files to show +# for several routes (files_ip, files_sp, files_rbp, search, viewlist) +################################################################################ +def GetEntries( OPT ): + entries=[] + if 'search_term' in request.form: + search_term=request.form['search_term'] + if 'AI:' in search_term: + search_term = search_term.replace('AI:','') + all_entries = Entry.query.join(File).join(FaceFileLink).join(Face).join(FaceRefimgLink).join(Refimg).join(PersonRefimgLink).join(Person).filter(Person.tag.ilike(f"%{search_term}%")).order_by(File.year.desc(),File.month.desc(),File.day.desc(),Entry.name).offset(OPT['offset']).limit(OPT['how_many']).all() + else: + file_data=Entry.query.join(File).filter(Entry.name.ilike(f"%{search_term}%")).order_by(File.year.desc(),File.month.desc(),File.day.desc(),Entry.name).offset(OPT['offset']).limit(OPT['how_many']).all() + dir_data=Entry.query.join(File).join(EntryDirLink).join(Dir).filter(Dir.rel_path.ilike(f"%{search_term}%")).order_by(File.year.desc(),File.month.desc(),File.day.desc(),Entry.name).offset(OPT['offset']).limit(OPT['how_many']).all() + ai_data=Entry.query.join(File).join(FaceFileLink).join(Face).join(FaceRefimgLink).join(Refimg).join(PersonRefimgLink).join(Person).filter(Person.tag.ilike(f"%{search_term}%")).order_by(File.year.desc(),File.month.desc(),File.day.desc(),Entry.name).offset(OPT['offset']).limit(OPT['how_many']).all() + all_entries = file_data + dir_data + ai_data + return all_entries + + for path in OPT['paths']: + if not os.path.exists(path): + continue + prefix = SymlinkName(OPT['path_type'],path,path+'/') + if OPT['folders']: + entries+=GetEntriesInFolderView( OPT, prefix ) + else: + entries+=GetEntriesInFlatView( OPT, prefix ) return entries ################################################################################ @@ -271,15 +327,9 @@ def GetEntriesInFolderView( cwd, prefix, noo, offset, how_many ): @app.route("/file_list_ip", methods=["GET","POST"]) @login_required def file_list_ip(): - noo, grouping, how_many, offset, size, folders, cwd, root = ViewingOptions( request ) - entries=[] - - settings=Settings.query.first() - paths = settings.import_path.split("#") - for path in paths: - prefix = SymlinkName("Import",path,path+'/') - entries+=GetEntriesInFlatView( cwd, prefix, noo, offset, how_many ) - return render_template("file_list.html", page_title='View File Details (Import Path)', entry_data=entries, noo=noo, how_many=how_many, offset=offset ) + OPT=SetViewingOptions( request ) + entries=GetEntries( OPT ) + return render_template("file_list.html", page_title='View File Details (Import Path)', entry_data=entries, OPT=OPT ) ################################################################################ # /files -> show thumbnail view of files from import_path(s) @@ -287,26 +337,10 @@ def file_list_ip(): @app.route("/files_ip", methods=["GET", "POST"]) @login_required def files_ip(): - - noo, grouping, how_many, offset, size, folders, cwd, root = ViewingOptions( request ) - entries=[] - + OPT=SetViewingOptions( request ) + entries=GetEntries( OPT ) people = Person.query.all() - - - # per import path, add entries to view - settings=Settings.query.first() - paths = settings.import_path.split("#") - for path in paths: - if not os.path.exists(path): - continue - prefix = SymlinkName("Import",path,path+'/') - if folders: - entries+=GetEntriesInFolderView( cwd, prefix, noo, offset, how_many ) - else: - entries+=GetEntriesInFlatView( cwd, prefix, noo, offset, how_many ) - - return render_template("files.html", page_title='View Files (Import Path)', entry_data=entries, noo=noo, grouping=grouping, how_many=how_many, offset=offset, size=size, folders=folders, cwd=cwd, root=root, people=people ) + return render_template("files.html", page_title=f"View Files ({OPT['path_type']} Path)", entry_data=entries, OPT=OPT, people=people ) ################################################################################ # /files -> show thumbnail view of files from storage_path @@ -314,24 +348,10 @@ def files_ip(): @app.route("/files_sp", methods=["GET", "POST"]) @login_required def files_sp(): - noo, grouping, how_many, offset, size, folders, cwd, root = ViewingOptions( request ) - entries=[] - + OPT=SetViewingOptions( request ) + entries=GetEntries( OPT ) people = Person.query.all() - - # per storage path, add entries to view - settings=Settings.query.first() - paths = settings.storage_path.split("#") - for path in paths: - if not os.path.exists(path): - continue - prefix = SymlinkName("Storage",path,path+'/') - if folders: - entries+=GetEntriesInFolderView( cwd, prefix, noo, offset, how_many ) - else: - entries+=GetEntriesInFlatView( cwd, prefix, noo, offset, how_many ) - - return render_template("files.html", page_title='View Files (Storage Path)', entry_data=entries, noo=noo, grouping=grouping, how_many=how_many, offset=offset, size=size, folders=folders, cwd=cwd, root=root, people=people ) + return render_template("files.html", page_title=f"View Files ({OPT['path_type']} Path)", entry_data=entries, OPT=OPT, people=people ) ################################################################################ @@ -340,22 +360,10 @@ def files_sp(): @app.route("/files_rbp", methods=["GET", "POST"]) @login_required def files_rbp(): - noo, grouping, how_many, offset, size, folders, cwd, root = ViewingOptions( request ) - entries=[] - - # per recyle bin path, add entries to view - settings=Settings.query.first() - paths = settings.recycle_bin_path.split("#") - for path in paths: - if not os.path.exists(path): - continue - prefix = SymlinkName("Bin",path,path+'/') - if folders: - entries+=GetEntriesInFolderView( cwd, prefix, noo, offset, how_many ) - else: - entries+=GetEntriesInFlatView( cwd, prefix, noo, offset, how_many ) - - return render_template("files.html", page_title='View Files (Bin Path)', entry_data=entries, noo=noo, grouping=grouping, how_many=how_many, offset=offset, size=size, folders=folders, cwd=cwd, root=root ) + OPT=SetViewingOptions( request ) + entries=GetEntries( OPT ) + people = Person.query.all() + return render_template("files.html", page_title=f"View Files ({OPT['path_type']} Path)", entry_data=entries, OPT=OPT ) ################################################################################ @@ -364,22 +372,11 @@ def files_rbp(): @app.route("/search", methods=["GET","POST"]) @login_required def search(): - - noo, grouping, how_many, offset, size, folders, cwd, root = ViewingOptions( request ) + OPT=SetViewingOptions( request ) # always show flat results for search to start with - folders=False - - term=request.form['term'] - if 'AI:' in term: - term = term.replace('AI:','') - all_entries = Entry.query.join(File).join(FaceFileLink).join(Face).join(FaceRefimgLink).join(Refimg).join(PersonRefimgLink).join(Person).filter(Person.tag.ilike(f"%{term}%")).order_by(File.year.desc(),File.month.desc(),File.day.desc(),Entry.name).offset(offset).limit(how_many).all() - else: - file_data=Entry.query.join(File).filter(Entry.name.ilike(f"%{request.form['term']}%")).order_by(File.year.desc(),File.month.desc(),File.day.desc(),Entry.name).offset(offset).limit(how_many).all() - dir_data=Entry.query.join(File).join(EntryDirLink).join(Dir).filter(Dir.rel_path.ilike(f"%{request.form['term']}%")).order_by(File.year.desc(),File.month.desc(),File.day.desc(),Entry.name).offset(offset).limit(how_many).all() - ai_data=Entry.query.join(File).join(FaceFileLink).join(Face).join(FaceRefimgLink).join(Refimg).join(PersonRefimgLink).join(Person).filter(Person.tag.ilike(f"%{request.form['term']}%")).order_by(File.year.desc(),File.month.desc(),File.day.desc(),Entry.name).offset(offset).limit(how_many).all() - all_entries = file_data + dir_data + ai_data - - return render_template("files.html", page_title='View Files', search_term=request.form['term'], entry_data=all_entries, noo=noo, grouping=grouping, how_many=how_many, offset=offset, size=size, folders=folders, cwd=cwd, root=root ) + OPT['folders']=False + entries=GetEntries( OPT ) + return render_template("files.html", page_title='View Files', search_term=request.form['search_term'], entry_data=entries, OPT=OPT ) ################################################################################ # /files/scannow -> allows us to force a check for new files @@ -520,73 +517,62 @@ def move_files(): return render_template("base.html") ################################################################################ -# /viewnext -> moves to the next entry and grabs data from DB and views it +# /viewlist -> get new set of eids and set current to new img to view ################################################################################ -@app.route("/viewnext", methods=["GET","POST"]) +@app.route("/viewlist", methods=["POST"]) @login_required -def viewnext(): - sels={} - sels['fname']='true' - sels['faces']='true' - sels['distance']='true' - if request.method=="POST": - id = request.form['current'] - eids=request.form['eids'] - sels['fname']=request.form['fname'] - sels['faces']=request.form['faces'] - sels['distance']=request.form['distance'] - lst = eids.split(',') - new_id = lst[lst.index(id)+1] - obj = Entry.query.join(File).filter(Entry.id==new_id).first() +def viewlist(): + OPT=SetViewingOptions( request ) + # Get next/prev set of data - e.g. if next set, then it will use orig_url + # to go forward how_many from offset and then use viewer.html to show that + # first obj of the new list of entries + entries=GetEntries( OPT ) + # this occurs when we went from the last image on a page (with how_many on + # it) and it just happened to also be the last in the DB... + if not entries: + # undo the skip by how_many and getentries again + OPT['offset'] -= int(OPT['how_many']) + entries=GetEntries( OPT ) + # now flag we are at the last in db, to reset current below + OPT['last_entry_in_db']=1 + objs = {} + eids="" + for e in entries: + objs[e.id]=e + # get new eids for viewer.html + eids=eids+f"{e.id}," # put locn data back into array format - for face in obj.file_details.faces: + for face in e.file_details.faces: face.locn = json.loads(face.locn) - return render_template("viewer.html", obj=obj, eids=eids, sels=sels ) - -################################################################################ -# /viewprev -> moves to the prev entry and grabs data from DB and views it -################################################################################ -@app.route("/viewprev", methods=["GET","POST"]) -@login_required -def viewprev(): - sels={} - sels['fname']='true' - sels['faces']='true' - sels['distance']='true' - if request.method=="POST": - id = request.form['current'] - eids=request.form['eids'] - sels['fname']=request.form['fname'] - sels['faces']=request.form['faces'] - sels['distance']=request.form['distance'] - lst = eids.split(',') - new_id = lst[lst.index(id)-1] - obj = Entry.query.join(File).filter(Entry.id==new_id).first() - # put locn data back into array format - for face in obj.file_details.faces: - face.locn = json.loads(face.locn) - return render_template("viewer.html", obj=obj, eids=eids, sels=sels ) + eids=eids.rstrip(",") + lst = eids.split(',') + if 'next' in request.form: + current = int(lst[0]) + if 'prev' in request.form: + current = int(lst[-1]) + if 'last_entry_in_db' in OPT: + # force this back to the last image of the last page - its the last in the DB, so set OPT for it + current = int(lst[-1]) + OPT['last_entry_in_db']=current + return render_template("viewer.html", current=current, eids=eids, objs=objs, OPT=OPT ) + ################################################################################ # /view/id -> grabs data from DB and views it ################################################################################ -@app.route("/view/", methods=["GET","POST"]) +@app.route("/view/", methods=["POST"]) @login_required def view_img(id): - obj = Entry.query.join(File).filter(Entry.id==id).first() - # put locn data back into array format - for face in obj.file_details.faces: - face.locn = json.loads(face.locn) - - if request.method=="POST": - eids=request.form['eids'] - else: - eids='' - sels={} - sels['fname']='true' - sels['faces']='true' - sels['distance']='true' - return render_template("viewer.html", obj=obj, eids=eids, sels=sels ) + OPT=SetViewingOptions( request ) + eids=request.form['eids'].rstrip(',') + objs = {} + lst = eids.split(',') + for e in Entry.query.join(File).filter(Entry.id.in_(lst)).all(): + objs[e.id]=e + # put locn data back into array format + for face in e.file_details.faces: + face.locn = json.loads(face.locn) + return render_template("viewer.html", current=int(id), eids=eids, objs=objs, OPT=OPT ) # route called from front/end - if multiple images are being rotated, each rotation == a separate call # to this route (and therefore a separate rotate job. Each reponse allows the f/e to check the @@ -652,7 +638,7 @@ def custom_static(filename): ############################################################################### # This func creates a new filter in jinja2 to test to see if the Dir being -# checked, is a top-level folder of 'cwd' +# checked, is a top-level folder of 'OPT['cwd']' ################################################################################ @app.template_filter('TopLevelFolderOf') def _jinja2_filter_toplevelfolderof(path, cwd): diff --git a/templates/base.html b/templates/base.html index cd3b649..67baff0 100644 --- a/templates/base.html +++ b/templates/base.html @@ -107,7 +107,7 @@ - + diff --git a/templates/file_list.html b/templates/file_list.html index b2440e2..6f356cd 100644 --- a/templates/file_list.html +++ b/templates/file_list.html @@ -2,26 +2,26 @@

{{page_title}}

- +
- {{CreateSelect( "noo", noo, ["Oldest", "Newest","A to Z", "Z to A"], "$('#offset').val(0)", "rounded-start py-1 my-1")|safe }} - {{CreateSelect( "how_many", how_many, ["10", "25", "50", "75", "100", "150", "200", "500"], "", "rounded-end py-1 my-1" )|safe }} + {{CreateSelect( "noo", OPT['noo'], ["Oldest", "Newest","A to Z", "Z to A"], "$('#offset').val(0)", "rounded-start py-1 my-1")|safe }} + {{CreateSelect( "how_many", OPT['how_many'], ["10", "25", "50", "75", "100", "150", "200", "500"], "", "rounded-end py-1 my-1" )|safe }}
{% set prv_disabled="" %} - {% if offset|int == 0 %} + {% if OPT['offset']|int == 0 %} {% set prv_disabled="disabled" %} {% endif %} -  {{how_many}} files  +  {{OPT['how_many']}} files  {% set nxt_disabled="" %} - {% if entry_data|length < how_many|int %} + {% if entry_data|length < OPT['how_many']|int %} {% set nxt_disabled="disabled" %} {% endif %} -  {{how_many}} files  +  {{OPT['how_many']}} files  {% set nxt_disabled="" %} - {% if entry_data|length < how_many|int %} + {% if entry_data|length < OPT['how_many']|int %} {% set nxt_disabled="disabled" %} {% endif %}
- {% if size == "64" %} + {% if OPT['size'] == "64" %} {% set bt="btn-info text-white" %} {% else %} {% set bt="btn-outline-info" %} {% endif %} - {% if size == "96" %} + {% if OPT['size'] == "96" %} {% set bt="btn-info text-white" %} {% else %} {% set bt="btn-outline-info" %} {% endif %} - {% if size == "128" %} + {% if OPT['size'] == "128" %} {% set bt="btn-info text-white" %} {% else %} {% set bt="btn-outline-info" %} {% endif %} - {% if size == "192" %} + {% if OPT['size'] == "192" %} {% set bt="btn-info text-white" %} {% else %} {% set bt="btn-outline-info" %} {% endif %} - {% if size == "256" %} + {% if OPT['size'] == "256" %} {% set bt="btn-info text-white" %} {% else %} {% set bt="btn-outline-info" %} @@ -104,8 +104,8 @@
- - + +
{% set eids=namespace( str="" ) %} {# gather all the file eids and collect them in case we go gallery mode #} @@ -120,59 +120,59 @@
{% set last = namespace(printed=0) %} {# rare event of empty folder, still need to show back button #} - {% if folders and entry_data|length == 0 %} - {% if cwd != root %} -
- + {% if OPT['folders'] and entry_data|length == 0 %} + {% if OPT['cwd'] != OPT['root'] %} +
+
Back
{% else %}
- +
{% endif %} {% endif %} {% for obj in entry_data %} - {% if loop.index==1 and folders %} - {% if cwd != root %} -
- + {% if loop.index==1 and OPT['folders'] %} + {% if OPT['cwd'] != OPT['root'] %} +
+
Back
{% else %} {# create an even lighter-grey, unclickable back button - so folders dont jump around when you go into them #}
- +
{% endif %} {% endif %} - {% if not folders and obj.type.name == "Directory" %} + {% if not OPT['folders'] and obj.type.name == "Directory" %} {% continue %} {% endif %} - {% if grouping == "Day" %} + {% if OPT['grouping'] == "Day" %} {% if last.printed != obj.file_details.day %}
Day: {{obj.file_details.day}} of {{obj.file_details.month}}/{{obj.file_details.year}}
{% set last.printed = obj.file_details.day %} {% endif %} - {% elif grouping == "Week" %} + {% elif OPT['grouping'] == "Week" %} {% if last.printed != obj.file_details.woy %}
Week #: {{obj.file_details.woy}} of {{obj.file_details.year}}
{% set last.printed = obj.file_details.woy %} {% endif %} - {% elif grouping == "Month" %} + {% elif OPT['grouping'] == "Month" %} {% if last.printed != obj.file_details.month %}
Month: {{obj.file_details.month}} of {{obj.file_details.year}}
{% set last.printed = obj.file_details.month %} {% endif %} {% endif %} {% if obj.type.name != "Directory" %} - {% if (not folders) or ((obj.in_dir.in_path.path_prefix+'/'+obj.in_dir.rel_path+'/'+obj.name) | TopLevelFolderOf(cwd)) %} + {% if (not OPT['folders']) or ((obj.in_dir.in_path.path_prefix+'/'+obj.in_dir.rel_path+'/'+obj.name) | TopLevelFolderOf(OPT['cwd'])) %}
{% if obj.type.name=="Image" %}
- + {% if search_term is defined %}
@@ -184,7 +184,7 @@
{% elif obj.type.name == "Video" %}
- +
@@ -198,16 +198,16 @@
{% endif %} {% else %} - {% if folders %} + {% if OPT['folders'] %} {% if obj.dir_details.rel_path | length %} {% set dirname=obj.dir_details.in_path.path_prefix+'/'+obj.dir_details.rel_path %} {% else %} {% set dirname=obj.dir_details.in_path.path_prefix %} {% endif %} {# if this dir is the toplevel of the cwd, show the folder icon #} - {% if dirname| TopLevelFolderOf(cwd) %} + {% if dirname| TopLevelFolderOf(OPT['cwd']) %}
- +
{{obj.name}}
@@ -220,13 +220,13 @@
- +
-  {{how_many}} files  +  {{OPT['how_many']}} files  @@ -242,13 +242,32 @@ $('.figure').click( function(e) { DoSel(e, this ); SetButtonState(); return false; }); $(document).on('click', function(e) { $('.highlight').removeClass('highlight') ; SetButtonState() }); -$('.figure').dblclick( - function() { - s='' - $(s).appendTo('body').submit(); } - ); +function CallViewRouteWrapper() +{ + CallViewRoute( $(this).attr("id") ) +} +function CallViewRoute(id) +{ + s='
' + s+='' + s+='' + s+='' + s+='' + s+='' + s+='' + s+='' + s+='' + s+='' + s+='' + {% if search_term is defined %} + s+='' + {% endif %} + s+='
' + $(s).appendTo('body').submit(); +} + +$('.figure').dblclick( CallViewRouteWrapper ) // different context menu on files $.contextMenu({ @@ -307,11 +326,7 @@ $.contextMenu({ return { callback: function( key, options) { if( key == "details" ) { DetailsDBox() } - if( key == "view" ) { - s='
' - $(s).appendTo('body').submit(); - } + if( key == "view" ) { CallViewRoute( $(this).attr('id') ) } if( key == "move" ) { MoveDBox() } if( key == "del" ) { DelDBox('Delete') } if( key == "undel") { DelDBox('Restore') } @@ -329,7 +344,7 @@ $.contextMenu({ $(document).ready(function() { - if( {{offset}} == 0 ) + if( {{OPT['offset']}} == 0 ) { $('.prev').addClass('disabled') $('.prev').prop('disabled', true) diff --git a/templates/viewer.html b/templates/viewer.html index 39945e8..e3a18b1 100644 --- a/templates/viewer.html +++ b/templates/viewer.html @@ -21,6 +21,33 @@ var grayscale=0 var throbber=0 + var objs=[] + var current={{current}} + var eids="{{eids}}" + var eid_lst=eids.split(",") + + {% for id in objs %} + e=new Object() + e.name = "{{objs[id].name}}" + e.url = "{{objs[id].FullPathOnFS()}}" + {% if objs[id].file_details.faces %} + e.face_model="{{objs[id].file_details.faces[0].facefile_lnk.model_used}}" + {% endif %} + e.faces=[] + {% for face in objs[id].file_details.faces %} + data = { + 'x': '{{face.locn[3]}}', 'y': '{{face.locn[0]}}', + 'w': '{{face.locn[1]-face.locn[3]}}', 'h':'{{face.locn[2]-face.locn[0]}}' + } + {% if face.refimg %} + data['who']='{{face.refimg.person.tag}}' + data['distance']="{{face.refimg_lnk.face_distance|round(2)}}" + {% endif %} + e.faces.push( data ) + {% endfor %} + objs[{{id}}]=e + {% endfor %} + function NewWidth() { w_r=im.width/(window.innerWidth*gap) @@ -61,20 +88,48 @@ else $('#throbber').hide(); + + // show (or not) the whole figcaption with fname in it - based on state of fname_toggle + if( $('#fname_toggle').prop('checked' ) ) + { + $('.figcaption').attr('style', 'display:show' ) + // reset fname for new image (if navigated left/right to get here) + $('#fname').html(objs[current].name) + } + else + $('.figcaption').attr('style', 'display:none' ) + + // if we have faces, the enable the toggles, otherwise disable them + // and reset model select too + if( objs[current].faces.length ) + { + $('#faces').attr('disabled', false) + $('#distance').attr('disabled', false) + $('#model').val( Number(objs[current].face_model) ) + } + else + { + $('#faces').attr('disabled', true) + $('#distance').attr('disabled', true) + // if no faces, then model is N/A (always 1st element - or 0 in select) + $('#model').val(0) + } + + // okay, we want faces drawn so lets do it if( $('#faces').prop('checked') ) { // draw rect on each face - for( i=0; i' + s+='' + s+='' + s+='' + s+='' + s+='' + s+='' + s+='' + s+='' + s+='' + s+='' + s+='' + {% if search_term is defined %} + s+='' + {% endif %} + s+='' + $(s).appendTo('body') + $('#_fmv').submit(); + } +
+ {% set max=eids.split(',')|length %}
- {% if eids.find(obj.id|string) > 0 %} -
- - - - - - -
- {% endif %} - {% if obj.type.name == "Image" %} -
- - + {% if objs[current].type.name == "Image" %} +
+ + + +
{{objs[current].name}}
+
+ {% elif objs[current].type.name == "Video" %} + - -
- {% elif obj.type.name == "Video" %} - - - {% endif %} - - {% for eid in eids.split(',') %} - {% if loop.index == max-1 %} - {% if eid|int != obj.id %} -
- - - - - - -
- {% endif %} {% endif %} - {% endfor %} + +
{# use this for color of toggles: https://www.codeply.com/p/4sL9uhevwJ #}
{# this whole div, just takes up the same space as the left button and is hidden for alignment only #} -
-
- Show: + Show:
- - + +
- +
- +
AI Model: - {% if not obj.file_details.faces %} - {{CreateSelect( "model", 0, ["N/A", "normal", "slow/accurate"], "", "rounded norm-txt", [0,1,2])|safe }} - {% else %} - {{CreateSelect( "model", obj.file_details.faces[0].facefile_lnk.model_used, ["normal", "slow/accurate"], "", "rounded norm-txt", [1,2])|safe }} - {% endif %} + {# can use 0 as default, it will be (re)set correctly in DrawImg() anyway #} + {{CreateSelect( "model", 0, ["N/A", "normal", "slow/accurate"], "", "rounded norm-txt", [0,1,2])|safe }}
-
@@ -300,5 +360,8 @@ $( document ).keydown(function(event) { return; // Quit when this doesn't handle the key event. } }); +{% if OPT['fullscreen']=='true' %} +$( document ).ready ( function() { document.getElementById('canvas').requestFullscreen() } ) +{% endif %} {% endblock script_content %}