From 9e9ad59bb6dc7303f69bd59bf57f2af7ba31ebb7 Mon Sep 17 00:00:00 2001 From: Eliot Lash Date: Tue, 14 Jan 2025 23:47:43 -0800 Subject: [PATCH] A few major fixes and improvements: - Perform feed matching by feed URL instead of fuzzy matching which seems to have 100% success rate, old method wasn't working at all for me. - Ignore uninitialized PA feeds - Add support for transferring in-progress episodes - Fix bug in episode URL match fallback logic where it wasn't doing anything when a match was found, and add log message when URL match is successful - Fix error from referencing SimpleChapters.type column which doesn't exist in AP 3.6.1 - Remove some unnecessary repeated calls to print function where \n literal could be used instead. --- pa_to_ap.py | 89 +++++++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/pa_to_ap.py b/pa_to_ap.py index 05b5c4c..3b34d76 100644 --- a/pa_to_ap.py +++ b/pa_to_ap.py @@ -26,6 +26,7 @@ class Feed: description: str author: str keep_updated: int + feed_url: str folder_name: str = '' @@ -77,43 +78,29 @@ def transfer(podcast_addict_cur: Cursor, antenna_pod_cur: Cursor): # first find match for all feeds in pa pa_feeds = [Feed(*a) for a in podcast_addict_cur.execute( 'select _id, name, description, author, ' - 'automaticRefresh, folderName from podcasts ' - 'where subscribed_status = 1 and is_virtual = 0')] + 'automaticRefresh, feed_url, folderName from podcasts ' + 'where subscribed_status = 1 and is_virtual = 0 and initialized_status = 1')] print("# Podcast addict feeds:") for feed in pa_feeds: print(feed.name) - print() - print() - - ap_feeds = [Feed(*a) for a in antenna_pod_cur.execute( - 'select id, title, description, author, keep_updated from Feeds ' - 'where downloaded = 1')] - - feed_attr_to_weight = { # - (lambda f: f.name): 0.85, # - (lambda f: f.author): 0.15, # - } - matcher = ObjectListMatcher(feed_attr_to_weight) + print("\n\n") - # should never be larger than the largest weight (otherwise is - # slightly unpredictable, as not every weight will be evaluated) - # value in range [0, 1] - matcher.minimum_similarity = 0.78 + ap_feeds = {a[5]: Feed(*a) for a in antenna_pod_cur.execute( + 'select id, title, description, author, keep_updated, download_url from Feeds ' + )} pa_to_ap = [] - ap_indices = matcher.get_indices(pa_feeds, ap_feeds) for n, pa in enumerate(pa_feeds): - ap_idx = ap_indices[n] - ap_name = '!!! NO MATCH !!!' - if ap_idx >= 0: - ap = ap_feeds[ap_idx] + pa_name = pa.name if pa.name else pa.feed_url + if pa.feed_url in ap_feeds: + ap = ap_feeds[pa.feed_url] ap_name = ap.name pa_to_ap.append((pa, ap)) - print(pa.name, ap_name, sep=" -> ") + print(pa_name, ap_name, sep=" -> ") print() if not confirmed("Is this correct? Can we continue?"): @@ -154,10 +141,10 @@ def transfer_from_feed_to_feed(podcast_addict_cur: Cursor, # pa_episodes = list(podcast_addict_cur.execute( # # 0 1 n2 n3 n4 'select _id, name, seen_status, favorite, local_file_name, ' - # n5 n6 n7 n8 - 'playbackDate, duration_ms, chapters_extracted, download_url ' + # n5 n6 n7 n8 n9 + 'playbackDate, duration_ms, chapters_extracted, download_url, position_to_resume ' 'from episodes where podcast_id = ? and ' - '(seen_status = 1 or ' + '(seen_status = 1 or position_to_resume < 0 or ' '(local_file_name != "" and local_file_name IS NOT NULL))', (pa.id,))) @@ -165,13 +152,11 @@ def transfer_from_feed_to_feed(podcast_addict_cur: Cursor, # 'select fi.id, fi.title, fm.download_url ' 'from FeedItems fi ' 'LEFT JOIN FeedMedia fm ON fi.id = fm.feeditem ' - 'where fi.feed = ? and fi.read = 0 ', (ap.id,))) + 'where fi.feed = ? and fi.read = 0 ' + , (ap.id,))) - print() combinations = len(pa_episodes) * len(ap_episodes) - print(f"Rough estimate: {combinations / 4000:.2f} seconds") - print() - print() + print(f"\nRough estimate: {combinations / 4000:.2f} seconds\n\n") pa_indices = ITEM_MATCHER.get_indices(ap_episodes, pa_episodes) seen_match_count = 0 @@ -185,13 +170,15 @@ def transfer_from_feed_to_feed(podcast_addict_cur: Cursor, # if MATCH_ON_EPISODE_URL_IF_COULD_NOT_FIND_A_MATCH_OTHERWISE and ap_url is not None: ap_url = ap_url.strip() if len(ap_url) > 9: - for pa_idx, pa_ep in enumerate(pa_episodes): + for pa_idx_urlmatch, pa_ep in enumerate(pa_episodes): if not pa_ep[8]: continue pa_url = pa_ep[8].strip() if pa_url and pa_url == ap_url: + print(f"! Fallback to URL match for: {ap_ep[1]}") found = True + pa_idx = pa_idx_urlmatch break if not found: @@ -203,6 +190,10 @@ def transfer_from_feed_to_feed(podcast_addict_cur: Cursor, # if pa_ep[2]: transfer_from_seen_ep_to_ep(antenna_pod_cur, podcast_addict_cur, # pa_ep, ap_ep) + else: + transfer_progress_ep_to_ep(antenna_pod_cur, podcast_addict_cur, # + pa_ep, ap_ep) + if pa_ep[3]: antenna_pod_cur.execute( @@ -227,10 +218,9 @@ def transfer_chapters(antenna_pod_cur: Cursor, # for title, start in podcast_addict_cur.execute( # "select name, start from chapters " "where podcastId = ? and episodeId = ?", (pa_feed_id, pa_ep[0])): - # we use chapter type 2 (id3) simply because it seems most likely antenna_pod_cur.execute("INSERT INTO SimpleChapters " - "(title, start, feeditem, type) VALUES " - "(?, ?, ?, 2)", (title, start, ap_ep[0],)) + "(title, start, feeditem) VALUES " + "(?, ?, ?)", (title, start, ap_ep[0])) def transfer_from_dld_ep_to_ep(antenna_pod_cur: Cursor, # @@ -238,7 +228,7 @@ def transfer_from_dld_ep_to_ep(antenna_pod_cur: Cursor, # pa_ep: tuple, # ap_ep: tuple, # pa_folder_name: str): - pa_ep_id, _, _, _, pa_local_file_name, _, _, _, _ = pa_ep + pa_ep_id, _, _, _, pa_local_file_name, _, _, _, _, _ = pa_ep dir_path = EPISODES_DIR_PATH.rstrip("/") + "/" + pa_folder_name file_path = dir_path + "/" + pa_local_file_name @@ -253,8 +243,8 @@ def transfer_from_seen_ep_to_ep(antenna_pod_cur: Cursor, # podcast_addict_cur: Cursor, # pa_ep: tuple, # ap_ep: tuple): - print(ap_ep[1], " <> ", pa_ep[1]) - pa_ep_id, _, _, _, _, pa_playbackDate, pa_duration_ms, _, _ = pa_ep + print(ap_ep[1], " <> ", pa_ep[1]) + pa_ep_id, _, _, _, _, pa_playbackDate, pa_duration_ms, _, _, _ = pa_ep antenna_pod_cur.execute("UPDATE FeedItems SET read = 1 WHERE id = ?", (ap_ep[0],)) @@ -266,13 +256,26 @@ def transfer_from_seen_ep_to_ep(antenna_pod_cur: Cursor, # (pa_playbackDate, pa_playbackDate, pa_duration_ms, ap_ep[0],)) +def transfer_progress_ep_to_ep(antenna_pod_cur: Cursor, + podcast_addict_cur: Cursor, + pa_ep: tuple, + ap_ep: tuple): + print(ap_ep[1], " <> ", pa_ep[1]) + pa_ep_id, _, _, _, _, pa_playbackDate, pa_duration_ms, _, _, pa_position = pa_ep + + antenna_pod_cur.execute("UPDATE FeedMedia " + "SET last_played_time = ?, " + "position = ?, " + "played_duration = ? " + "WHERE feeditem = ?", + (pa_playbackDate, pa_position, pa_position, + ap_ep[0],)) + ap_db, pa_db = get_antenna_pod_and_podcast_addict_backup_path() -print() -print("AntennaPod .db file found:", ap_db) +print("\nAntennaPod .db file found:", ap_db) print("Podcast Addict .db file found:", pa_db) -print() -print() +print("\n") podcast_addict_con = None antenna_pod_con = None