Last active
August 29, 2015 14:23
Automatically clean up the "Previous Mobile Applications" folder created by iTunes. https://zmwangx.github.io/blog/2015-06-27-automatically-clean-up-previous-mobile-applications.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
"""Periodically clean up "Previous Mobile Applications" of iTunes.""" | |
import arrow | |
import datetime | |
import json | |
import os | |
import sys | |
OFFENDING_DIR = os.path.expanduser("~/Music/iTunes/iTunes Media/Mobile Applications/Previous Mobile Applications") | |
STORAGE_DIR = os.path.expanduser("~/.local/share/itunes") | |
STORAGE_FILE = os.path.join(STORAGE_DIR, "previous-mobile-applications.json") | |
DELETE_AFTER = datetime.timedelta(days=7) | |
def load_storage(): | |
"""Load stored dictionary of seen apps from STORAGE_FILE. | |
Returns | |
------- | |
seen_app_dict : dict | |
Dictionary of (app_filename, first_seen_date) key-value pairs, | |
where app_filename is str, and last_seen_date is datetime.date. | |
""" | |
os.makedirs(STORAGE_DIR, mode=0o700, exist_ok=True) | |
try: | |
with open(STORAGE_FILE, encoding="utf-8") as fp: | |
serializable_seen_app_dict = json.load(fp) | |
return {app_filename: arrow.get(serialized_first_seen_date).date() | |
for app_filename, serialized_first_seen_date in serializable_seen_app_dict.items()} | |
except OSError: | |
return {} | |
def write_storage(seen_app_dict): | |
"""Write the dictionary of seen apps to STORAGE_FILE. | |
Parameters | |
---------- | |
seen_app_dict : dict | |
See the return format of load_storage(). | |
Returns | |
------- | |
0 or 1 | |
Return code indicating success or failure. | |
""" | |
# convert datetime.time to str (ISO 8601) | |
serializable_seen_app_dict = {app_filename: first_seen_date.isoformat() | |
for app_filename, first_seen_date in seen_app_dict.items()} | |
os.makedirs(STORAGE_DIR, mode=0o700, exist_ok=True) | |
try: | |
with open(STORAGE_FILE, mode="w", encoding="utf-8") as fp: | |
json.dump(serializable_seen_app_dict, fp, indent=2, sort_keys=True) | |
return 0 | |
except OSError as err: | |
sys.stderr.write("error: failed to write to '%s': %s" % (STORAGE_FILE, str(err))) | |
return 1 | |
def main(): | |
"""Main. | |
Returns | |
------- | |
0 or 1 | |
Return code indicating success or failure. | |
""" | |
if not os.path.isdir(OFFENDING_DIR): | |
# good, you don't have that junk | |
return 0 | |
today = datetime.date.today() | |
seen_app_dict = load_storage() | |
current_app_list = os.listdir(OFFENDING_DIR) | |
# boot already disappeared apps | |
for app in [app for app in seen_app_dict if app not in current_app_list]: | |
seen_app_dict.pop(app) | |
# add newly appeared apps | |
for app in [app for app in current_app_list if app not in seen_app_dict]: | |
seen_app_dict[app] = today | |
# delete expired apps | |
returncode = 0 | |
newly_deleted_apps = [] | |
for app in seen_app_dict: | |
if today >= seen_app_dict[app] + DELETE_AFTER: | |
app_path = os.path.join(OFFENDING_DIR, app) | |
try: | |
os.remove(app_path) | |
newly_deleted_apps.append(app) | |
except OSError as err: | |
sys.stderr.write("error: failed to remove '%s': %s" % (app_path, str(err))) | |
returncode = 1 | |
for app in newly_deleted_apps: | |
seen_app_dict.pop(app) | |
# write data to disk | |
returncode |= write_storage(seen_app_dict) | |
return returncode | |
if __name__ == "__main__": | |
exit(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment