@@ -1610,10 +1610,39 @@ def table_exists(conn, name):
16101610 cur .execute ("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?" , (name ,))
16111611 return cur .fetchone () is not None
16121612
1613- def column_exists (conn , table , col ):
1613+ def list_user_tables (conn ):
1614+ cur = conn .cursor ()
1615+ cur .execute ("""
1616+ SELECT name
1617+ FROM sqlite_master
1618+ WHERE type='table' AND name NOT LIKE 'sqlite_%'
1619+ ORDER BY name
1620+ """ )
1621+ return [r [0 ] for r in cur .fetchall ()]
1622+
1623+ def columns_lower (conn , table ):
16141624 cur = conn .cursor ()
16151625 cur .execute (f"PRAGMA table_info({ table } )" )
1616- return any (r [1 ].lower () == col .lower () for r in cur .fetchall ())
1626+ return [r [1 ].lower () for r in cur .fetchall ()]
1627+
1628+ def find_clip_table_and_title_col (conn ):
1629+ # Prefer common names first
1630+ preferred_tables = ["clips" , "music_clips" , "drive_clips" ]
1631+ title_candidates = ("title" , "name" , "label" , "clip_title" )
1632+ # 1) try preferred names
1633+ for t in preferred_tables :
1634+ if table_exists (conn , t ):
1635+ cols = columns_lower (conn , t )
1636+ for cand in title_candidates :
1637+ if cand in cols :
1638+ return t , cand
1639+ # 2) scan all tables for a title-like column
1640+ for t in list_user_tables (conn ):
1641+ cols = columns_lower (conn , t )
1642+ for cand in title_candidates :
1643+ if cand in cols :
1644+ return t , cand
1645+ return None , None
16171646
16181647 props = [] # [{slug, title, count}]
16191648 clip_titles = [] # [str]
@@ -1634,50 +1663,42 @@ def column_exists(conn, table, col):
16341663 for r in cur .fetchall ():
16351664 counts [r ["property" ]] = r ["c" ]
16361665
1637- # Properties: prefer properties table if present, else derive from uploads_log
1666+ # Prefer a ' properties' table if present; otherwise derive from uploads_log
16381667 if table_exists (conn , "properties" ):
1639- has_title = column_exists (conn , "properties" , "title" )
16401668 cur = conn .cursor ()
1641- if has_title :
1669+ # check if 'title' exists
1670+ cur .execute ("PRAGMA table_info(properties)" )
1671+ have_title = any (row [1 ].lower () == "title" for row in cur .fetchall ())
1672+ if have_title :
16421673 cur .execute ("SELECT slug, title FROM properties ORDER BY COALESCE(title, slug)" )
16431674 rows = cur .fetchall ()
16441675 for r in rows :
16451676 slug = r ["slug" ]
16461677 title = (r ["title" ] or "" ).strip () or PRETTY_FALLBACK .get (slug , slug .replace ("_" , " " ).title ())
16471678 props .append ({"slug" : slug , "title" : title , "count" : counts .get (slug , 0 )})
16481679 else :
1649- # No title column—use slug and prettify
16501680 cur .execute ("SELECT slug FROM properties ORDER BY slug" )
1651- rows = cur .fetchall ()
1652- for r in rows :
1653- slug = r ["slug" ]
1681+ for (slug ,) in cur .fetchall ():
16541682 title = PRETTY_FALLBACK .get (slug , slug .replace ("_" , " " ).title ())
16551683 props .append ({"slug" : slug , "title" : title , "count" : counts .get (slug , 0 )})
16561684 else :
1657- # No properties table—fall back to whatever exists in uploads_log
1685+ # no properties table—derive from uploads_log
16581686 for slug , c in sorted (counts .items (), key = lambda t : t [0 ]):
16591687 title = PRETTY_FALLBACK .get (slug , slug .replace ("_" , " " ).title ())
16601688 props .append ({"slug" : slug , "title" : title , "count" : c })
16611689
1662- # Clips: pick a reasonable title-like column if available
1663- if table_exists (conn , "clips" ):
1690+ # Find clips table + title-like column automatically
1691+ clip_table , title_col = find_clip_table_and_title_col (conn )
1692+ if clip_table and title_col :
16641693 cur = conn .cursor ()
1665- # find a likely title column
1666- cur .execute ("PRAGMA table_info(clips)" )
1667- cols = [r [1 ].lower () for r in cur .fetchall ()]
1668- title_col = None
1669- for cand in ("title" , "name" , "label" ):
1670- if cand in cols :
1671- title_col = cand
1672- break
1673- if title_col :
1674- cur .execute (f"""
1675- SELECT DISTINCT { title_col } AS t
1676- FROM clips
1677- WHERE { title_col } IS NOT NULL AND TRIM({ title_col } )!=''
1678- ORDER BY t COLLATE NOCASE
1679- """ )
1680- clip_titles = [r ["t" ] for r in cur .fetchall () if (r ["t" ] or "" ).strip ()]
1694+ # Quote identifiers to be safe; SQLite is case-insensitive for identifiers.
1695+ cur .execute (f'''
1696+ SELECT DISTINCT "{ title_col } " AS t
1697+ FROM "{ clip_table } "
1698+ WHERE "{ title_col } " IS NOT NULL AND TRIM("{ title_col } ")!=''
1699+ ORDER BY t COLLATE NOCASE
1700+ ''' )
1701+ clip_titles = [r ["t" ] for r in cur .fetchall () if (r ["t" ] or "" ).strip ()]
16811702
16821703 return render_template ("available_keys.html" , props = props , clip_titles = clip_titles )
16831704##############################################################################################################################################################
@@ -1733,7 +1754,6 @@ def delete_dataset_file(property_name, tab, filename):
17331754 flash (f"Deleted entry: { filename } " )
17341755
17351756 return redirect (url_for ('property_detail' , property_name = property_name , tab = tab ))
1736-
17371757##############################################################################################################################################################
17381758
17391759@app .route ('/add_drive_clip' , methods = ['GET' , 'POST' ])
0 commit comments