mirror of
https://github.com/skoelle/icloud-contacts-sync.git
synced 2026-09-17 23:40:24 +00:00
sync photo and display photo
This commit is contained in:
@@ -20,6 +20,7 @@ CREATE TABLE IF NOT EXISTS contacts (
|
|||||||
anniversary DATE NULL,
|
anniversary DATE NULL,
|
||||||
notes TEXT NULL,
|
notes TEXT NULL,
|
||||||
photo_base64 LONGTEXT NULL,
|
photo_base64 LONGTEXT NULL,
|
||||||
|
photo_url VARCHAR(2048) NULL,
|
||||||
emails JSON NULL,
|
emails JSON NULL,
|
||||||
phones JSON NULL,
|
phones JSON NULL,
|
||||||
addresses JSON NULL,
|
addresses JSON NULL,
|
||||||
|
|||||||
+4
-4
@@ -76,7 +76,7 @@ def list_contacts(
|
|||||||
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
|
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
|
||||||
job_title, birthday, notes, emails, phones, addresses, urls, social_profiles, categories, updated_at
|
job_title, birthday, notes, photo_url, emails, phones, addresses, urls, social_profiles, categories, updated_at
|
||||||
FROM contacts {where_clause}{search_clause}
|
FROM contacts {where_clause}{search_clause}
|
||||||
ORDER BY full_name
|
ORDER BY full_name
|
||||||
LIMIT %s OFFSET %s""",
|
LIMIT %s OFFSET %s""",
|
||||||
@@ -98,7 +98,7 @@ def get_contact(contact_id: int, current_user: str = Depends(get_current_user)):
|
|||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
|
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
|
||||||
job_title, birthday, notes, emails, phones, addresses, urls, social_profiles, categories, updated_at
|
job_title, birthday, notes, photo_url, emails, phones, addresses, urls, social_profiles, categories, updated_at
|
||||||
FROM contacts {where_clause} {id_clause}""",
|
FROM contacts {where_clause} {id_clause}""",
|
||||||
params + [contact_id],
|
params + [contact_id],
|
||||||
)
|
)
|
||||||
@@ -121,7 +121,7 @@ def birthdays_today(current_user: str = Depends(get_current_user)):
|
|||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
|
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
|
||||||
job_title, birthday, notes, emails, phones, addresses, urls, social_profiles, categories, updated_at
|
job_title, birthday, notes, photo_url, emails, phones, addresses, urls, social_profiles, categories, updated_at
|
||||||
FROM contacts {where_clause} {month_day_clause}
|
FROM contacts {where_clause} {month_day_clause}
|
||||||
ORDER BY full_name""",
|
ORDER BY full_name""",
|
||||||
params + [today.month, today.day],
|
params + [today.month, today.day],
|
||||||
@@ -293,7 +293,7 @@ def web_contact(
|
|||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
|
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
|
||||||
job_title, birthday, notes, emails, phones, addresses, urls, social_profiles, categories, updated_at
|
job_title, birthday, notes, photo_url, emails, phones, addresses, urls, social_profiles, categories, updated_at
|
||||||
FROM contacts {where_clause} {id_clause}""",
|
FROM contacts {where_clause} {id_clause}""",
|
||||||
params + [contact_id],
|
params + [contact_id],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class ContactOut(BaseModel):
|
|||||||
job_title: str | None
|
job_title: str | None
|
||||||
birthday: date | None
|
birthday: date | None
|
||||||
notes: str | None
|
notes: str | None
|
||||||
|
photo_url: str | None
|
||||||
emails: list
|
emails: list
|
||||||
phones: list
|
phones: list
|
||||||
addresses: list
|
addresses: list
|
||||||
|
|||||||
@@ -65,6 +65,14 @@
|
|||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contact-photo {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.contact-name {
|
.contact-name {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -207,6 +215,9 @@
|
|||||||
{% if is_admin and contact.account %}
|
{% if is_admin and contact.account %}
|
||||||
<span class="badge badge-account">{{ contact.account }}</span>
|
<span class="badge badge-account">{{ contact.account }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if contact.photo_url %}
|
||||||
|
<img src="{{ contact.photo_url }}" alt="Foto" class="contact-photo" onerror="this.style.display='none'">
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if contact.birthday %}
|
{% if contact.birthday %}
|
||||||
|
|||||||
+15
-1
@@ -86,6 +86,19 @@ def parse_vcard(raw_text: str, account: str, etag: str | None = None) -> dict |
|
|||||||
|
|
||||||
categories = [c.strip() for c in vcard.categories.value] if hasattr(vcard, "categories") else []
|
categories = [c.strip() for c in vcard.categories.value] if hasattr(vcard, "categories") else []
|
||||||
|
|
||||||
|
photo_url = None
|
||||||
|
photo_base64 = None
|
||||||
|
if hasattr(vcard, "photo"):
|
||||||
|
photo_obj = vcard.photo
|
||||||
|
params = getattr(photo_obj, "params", {})
|
||||||
|
encoding = params.get("ENCODING") or params.get("encoding")
|
||||||
|
value = photo_obj.value
|
||||||
|
if isinstance(value, str) and value.startswith("http"):
|
||||||
|
photo_url = value
|
||||||
|
elif encoding and str(encoding).upper() == "B" and isinstance(value, bytes):
|
||||||
|
import base64
|
||||||
|
photo_base64 = base64.b64encode(value).decode("ascii")
|
||||||
|
|
||||||
birthday = str(vcard.bday.value)[:10] if hasattr(vcard, "bday") else None
|
birthday = str(vcard.bday.value)[:10] if hasattr(vcard, "bday") else None
|
||||||
|
|
||||||
org = None
|
org = None
|
||||||
@@ -110,7 +123,8 @@ def parse_vcard(raw_text: str, account: str, etag: str | None = None) -> dict |
|
|||||||
"birthday": birthday,
|
"birthday": birthday,
|
||||||
"anniversary": None,
|
"anniversary": None,
|
||||||
"notes": _scalar(_get(vcard, "note")),
|
"notes": _scalar(_get(vcard, "note")),
|
||||||
"photo_base64": None,
|
"photo_base64": photo_base64,
|
||||||
|
"photo_url": photo_url,
|
||||||
"emails": json.dumps(emails, ensure_ascii=False),
|
"emails": json.dumps(emails, ensure_ascii=False),
|
||||||
"phones": json.dumps(phones, ensure_ascii=False),
|
"phones": json.dumps(phones, ensure_ascii=False),
|
||||||
"addresses": json.dumps(addresses, ensure_ascii=False),
|
"addresses": json.dumps(addresses, ensure_ascii=False),
|
||||||
|
|||||||
Reference in New Issue
Block a user