-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapple-voice-memos.py
More file actions
executable file
·122 lines (98 loc) · 2.89 KB
/
apple-voice-memos.py
File metadata and controls
executable file
·122 lines (98 loc) · 2.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/usr/bin/env -S uv --quiet run --no-project --script --
# https://peps.python.org/pep-0723/
# https://github.com/astral-sh/uv
# /// script
# requires-python = ">=3.14,<4"
# dependencies = [
# "pytz >=2022.1",
# ]
# ///
"""
Description
"""
from ast import DictComp
from multiprocessing import context
import sys, locale, argparse, pathlib, urllib.parse, contextlib, sqlite3, datetime
import pytz
def main(
*,
# Example Options
example
):
locale.setlocale(locale.LC_ALL, "")
cloud_recordings_db = pathlib.Path.home() / "Library" / "Application Support" / "com.apple.voicememos" / "Recordings" / "CloudRecordings.db";
db_uri = urllib.parse.urljoin(cloud_recordings_db.as_uri(), "?mode=ro")
query = """
select ZFOLDER.ZENCRYPTEDNAME, ZDATE, ZDURATION, ZCUSTOMLABEL, ZPATH
from ZCLOUDRECORDING
left join ZFOLDER
on ZCLOUDRECORDING.ZFOLDER = ZFOLDER.Z_PK
"""
with contextlib.ExitStack() as stack:
conn = stack.enter_context(contextlib.closing(sqlite3.connect(db_uri, uri=True)))
cursor = conn.cursor()
stack.enter_context(contextlib.closing(cursor))
cursor.execute(query)
for folder, date, duration, label, path in cursor.fetchall():
date = datetime_from_coredata_float(date).astimezone()
print(
"\n{:%F %T %z} [{}] {}\n[{}] {}\n".format(
date,
format_duration(duration, compact=True),
label,
folder,
path,
),
sep="",
end="\n"
)
def datetime_from_coredata_float(f):
result = datetime.datetime(2001, 1, 1)
result += datetime.timedelta(seconds=f)
result = result.replace(tzinfo=datetime.timezone.utc)
return result
def format_duration(seconds, *, compact=False):
seconds = round(seconds)
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
if compact:
return f"{hours}h{minutes}m{seconds}s".removeprefix("0h").removeprefix("0m")
else:
return f"{hours}h {minutes:2}m {seconds:2}s"
def parse_args(*, args, prog):
parser = argparse.ArgumentParser(
prog=prog,
usage="%(prog)s [OPTIONS]...",
description=__doc__,
formatter_class=argparse.RawTextHelpFormatter,
fromfile_prefix_chars="@",
add_help=False,
)
the_default = "\ndefault: %(default)s"
options_generic = parser.add_argument_group("Generic Options")
options_generic.add_argument(
"--help", "-h",
action="help",
help="show help message and exit",
)
options_example = parser.add_argument_group("Example Options")
options_example.add_argument(
"--example", "-e", metavar="NUMBER",
action="store", dest="example", type=int, default=None,
help="an example option that accepts an integer" + the_default
)
opts = parser.parse_args(args)
return vars(opts)
def configure_logging(opts):
pass
def smain(argv=None):
if argv is None:
argv = sys.argv
try:
opts = parse_args(args=argv[1:], prog=argv[0])
configure_logging(opts)
return main(**opts)
except KeyboardInterrupt:
print(file=sys.stderr)
if __name__ == "__main__":
sys.exit(smain())