auto_remove_unplayed_episodes -> auto.cleanup.unplayed
[gpodder.git] / src / gpodder / common.py
blobdc94bbc50cdc87660450312a0b94813ee4ea93f4
1 # -*- coding: utf-8 -*-
3 # gPodder - A media aggregator and podcast client
4 # Copyright (c) 2005-2018 The gPodder Team
6 # gPodder is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # gPodder is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 # gpodder.common - Common helper functions for all UIs
21 # Thomas Perl <thp@gpodder.org>; 2012-08-16
24 import glob
25 import logging
26 import os
28 import gpodder
29 from gpodder import util
31 logger = logging.getLogger(__name__)
34 def clean_up_downloads(delete_partial=False):
35 """Clean up temporary files left behind by old gPodder versions
37 delete_partial - If True, also delete in-progress downloads
38 """
39 temporary_files = glob.glob('%s/*/.tmp-*' % gpodder.downloads)
41 if delete_partial:
42 temporary_files += glob.glob('%s/*/*.partial' % gpodder.downloads)
43 # youtube-dl creates .partial.* files for adaptive formats
44 temporary_files += glob.glob('%s/*/*.partial.*' % gpodder.downloads)
46 for tempfile in temporary_files:
47 util.delete_file(tempfile)
50 def find_partial_downloads(channels, start_progress_callback, progress_callback, finish_progress_callback):
51 """Find partial downloads and match them with episodes
53 channels - A list of all model.PodcastChannel objects
54 start_progress_callback - A callback(count) when partial files are searched
55 progress_callback - A callback(title, progress) when an episode was found
56 finish_progress_callback - A callback(resumable_episodes) when finished
57 """
58 # Look for partial file downloads, ignoring .partial.* files created by youtube-dl
59 partial_files = glob.glob(os.path.join(gpodder.downloads, '*', '*.partial'))
60 count = len(partial_files)
61 resumable_episodes = []
62 start_progress_callback(count)
63 if count:
64 candidates = [f[:-len('.partial')] for f in partial_files]
65 found = 0
67 for channel in channels:
68 for episode in channel.get_all_episodes():
69 filename = episode.local_filename(create=False, check_only=True)
70 if filename in candidates:
71 found += 1
72 progress_callback(episode.title, found / count)
73 candidates.remove(filename)
74 partial_files.remove(filename + '.partial')
76 if os.path.exists(filename):
77 # The file has already been downloaded;
78 # remove the leftover partial file
79 util.delete_file(filename + '.partial')
80 else:
81 resumable_episodes.append(episode)
83 if not candidates:
84 break
86 if not candidates:
87 break
89 for f in partial_files:
90 logger.warning('Partial file without episode: %s', f)
91 util.delete_file(f)
93 # never delete partial: either we can't clean them up because we offer to
94 # resume download or there are none to delete in the first place.
95 clean_up_downloads(delete_partial=False)
96 finish_progress_callback(resumable_episodes)
99 def get_expired_episodes(channels, config):
100 for channel in channels:
101 for index, episode in enumerate(channel.get_episodes(gpodder.STATE_DOWNLOADED)):
102 # Never consider archived episodes as old
103 if episode.archive:
104 continue
106 # Download strategy "Only keep latest"
107 if (channel.download_strategy == channel.STRATEGY_LATEST
108 and index > 0):
109 logger.info('Removing episode (only keep latest strategy): %s',
110 episode.title)
111 yield episode
112 continue
114 # Only expire episodes if the age in days is positive
115 if config.auto.cleanup.days < 1:
116 continue
118 # Never consider fresh episodes as old
119 if episode.age_in_days() < config.auto.cleanup.days:
120 continue
122 # Do not delete played episodes (except if configured)
123 if not episode.is_new:
124 if not config.auto.cleanup.played:
125 continue
127 # Do not delete unfinished episodes (except if configured)
128 if not episode.is_finished():
129 if not config.auto.cleanup.unfinished:
130 continue
132 # Do not delete unplayed episodes (except if configured)
133 if episode.is_new:
134 if not config.auto.cleanup.unplayed:
135 continue
137 yield episode