Skip to content

Commit f5b6422

Browse files
Merge pull request #32 from Keeper-of-the-Keys/episode-artwork
Episode artwork
2 parents 24f0938 + 857a4cc commit f5b6422

File tree

5 files changed

+66
-16
lines changed

5 files changed

+66
-16
lines changed

share/man/man1/gpo.1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.TH GPO "1" "March 2020" "gpodder 4.10.0" "User Commands"
1+
.TH GPO "1" "March 2020" "gpodder 4.11.0" "User Commands"
22
.SH NAME
33
gpo \- gPodder command-line interface
44
.SH SYNOPSIS

src/gpodder/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@
5050
# This metadata block gets parsed by setup.py - use single quotes only
5151
__tagline__ = 'Media and podcast aggregator'
5252
__author__ = 'Thomas Perl <[email protected]>'
53-
__version__ = '4.10.0'
54-
__date__ = '2020-03-03'
55-
__relname__ = 'Matar'
53+
__version__ = '4.11.0'
54+
__date__ = '2020-03-31'
55+
__relname__ = 'Geshem'
5656
__copyright__ = '© 2005-2020 Thomas Perl and the gPodder Team'
5757
__license__ = 'ISC / GPLv3 or later'
5858
__url__ = 'http://gpodder.org/'

src/gpodder/coverart.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,21 @@ class CoverDownloader(object):
4444
def __init__(self, core):
4545
self.core = core
4646

47-
def get_cover(self, podcast, download=False):
48-
filename = podcast.cover_file
49-
cover_url = podcast.cover_url
47+
def get_cover(self, podcast, download=False, episode=None):
48+
if episode:
49+
# Get episode art.
50+
filename = episode.art_file
51+
cover_url = episode.episode_art_url
52+
else:
53+
# Get podcast cover.
54+
filename = podcast.cover_file
55+
cover_url = podcast.cover_url
56+
57+
if not cover_url:
58+
return None
59+
60+
username = podcast.auth_username
61+
password = podcast.auth_password
5062

5163
# Return already existing files
5264
for extension in self.EXTENSIONS:
@@ -62,8 +74,7 @@ def get_cover(self, podcast, download=False):
6274

6375
# We have to add username/password, because password-protected
6476
# feeds might keep their cover art also protected (bug 1521)
65-
cover_url = util.url_add_authentication(cover_url, podcast.auth_username,
66-
podcast.auth_password)
77+
cover_url = util.url_add_authentication(cover_url, username, password)
6778

6879
try:
6980
logger.info('Downloading cover art: %s', cover_url)
@@ -80,7 +91,7 @@ def get_cover(self, podcast, download=False):
8091
extension = filetype
8192
break
8293

83-
if extension is None:
94+
if not extension:
8495
msg = 'Unknown file type: %s (%r)' % (cover_url, data[:6])
8596
raise ValueError(msg)
8697

src/gpodder/model.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class EpisodeModelFields(minidb.Model):
9494
chapters = minidb.JSON
9595
subtitle = str
9696
description_html = str
97+
episode_art_url = str
9798

9899

99100
class PodcastModelFields(minidb.Model):
@@ -148,6 +149,7 @@ class __minidb_defaults__:
148149
current_position = 0
149150
current_position_updated = 0
150151
last_playback = 0
152+
episode_art_url = ''
151153

152154
def __init__(self, channel):
153155
self._parent = channel
@@ -462,6 +464,17 @@ def update_from_dict(self, episode_dict):
462464
if k in episode_dict:
463465
setattr(self, k, episode_dict[k])
464466

467+
@property
468+
def art_file(self):
469+
if self.episode_art_url:
470+
filename, extension = util.filename_from_url(self.episode_art_url)
471+
472+
if not filename:
473+
filename = hashlib.sha512(self.episode_art_url.encode('utf-8')).hexdigest()
474+
475+
return os.path.join(self.podcast.save_dir, filename)
476+
return None
477+
465478

466479
class PodcastChannel(PodcastModelFields, PodcastModelMixin):
467480
_common_prefix = str
@@ -553,9 +566,15 @@ def check_download_folder(self):
553566

554567
known_files.add(filename)
555568

556-
known_files.update(os.path.join(self.save_dir, 'folder' + ext)
569+
known_files.update(os.path.join(self.cover_file + ext)
557570
for ext in coverart.CoverDownloader.EXTENSIONS)
558571

572+
for episode in self.episodes:
573+
filename = episode.art_file
574+
if filename:
575+
known_files.update(os.path.join(episode.art_file + ext)
576+
for ext in coverart.CoverDownloader.EXTENSIONS)
577+
559578
existing_files = {filename for filename in
560579
glob.glob(os.path.join(self.save_dir, '*'))
561580
if not filename.endswith('.partial')}
@@ -737,6 +756,10 @@ def _consume_custom_feed(self, custom_feed):
737756
# Add new episodes to episodes
738757
self.episodes.extend(new_episodes)
739758

759+
# Verify that all episode art is up-to-date
760+
for episode in self.episodes:
761+
self.model.core.cover_downloader.get_cover(self, download=True, episode=episode)
762+
740763
# Sort episodes by pubdate, descending
741764
self.episodes.sort(key=lambda e: e.published, reverse=True)
742765

@@ -753,8 +776,9 @@ def update(self):
753776
logger.info('URL updated: {} -> {}'.format(old_url, self.url))
754777
self._consume_custom_feed(result)
755778

756-
# Download the cover art if it's not yet available
757-
self.model.core.cover_downloader.get_cover(self, download=True)
779+
# Download the cover art if it's not yet available, don't run if no save_dir was created yet.
780+
if self.save_dir:
781+
self.model.core.cover_downloader.get_cover(self, download=True)
758782

759783
self.save()
760784

@@ -920,7 +944,12 @@ def remove_downloaded(self):
920944

921945
@property
922946
def cover_file(self):
923-
return os.path.join(self.save_dir, 'folder')
947+
if self.cover_url:
948+
filename, extension = util.filename_from_url(self.cover_url)
949+
if not filename:
950+
filename = hashlib.sha512(self.cover_url.encode('utf-8')).hexdigest()
951+
return os.path.join(self.save_dir, filename)
952+
return None
924953

925954

926955
class Model(object):

src/gpodder/plugins/soundcloud.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,12 @@ def get_user_info(self):
107107

108108
def get_coverart(self):
109109
user_info = self.get_user_info()
110-
return user_info.get('avatar_url', None)
110+
avatar_url = user_info.get('avatar_url', None)
111+
if avatar_url:
112+
# Soundcloud API by default returns the URL to "large" artwork - 100x100
113+
# by replacing "-large" with "-original" in the URL we get unresized files.
114+
return avatar_url.replace("-large", "-original")
115+
return avatar_url
111116

112117
def get_user_id(self):
113118
user_info = self.get_user_info()
@@ -159,7 +164,7 @@ def get_tracks(self, feed, channel):
159164
for track in tracks:
160165
# Prefer stream URL (MP3), fallback to download URL
161166
base_url = track.get('stream_url') if track['streamable'] else track.get('download_url')
162-
if base_url != None:
167+
if base_url:
163168
url = base_url + '?consumer_key=%(consumer_key)s' % {'consumer_key': CONSUMER_KEY}
164169
else:
165170
logger.debug('Skipping track with no base_url')
@@ -174,6 +179,10 @@ def get_tracks(self, feed, channel):
174179
filetype = self.cache['episodes'][track_guid]['filetype']
175180
read_from_cache += 1
176181

182+
artwork_url = track.get('artwork_url')
183+
if artwork_url:
184+
artwork_url = artwork_url.replace("-large", "-original")
185+
177186
yield {
178187
'title': track.get('title', track.get('permalink')) or ('Unknown track'),
179188
'link': track.get('permalink_url') or 'https://soundcloud.com/' + self.username,
@@ -184,6 +193,7 @@ def get_tracks(self, feed, channel):
184193
'guid': track_guid,
185194
'published': soundcloud_parsedate(track.get('created_at', None)),
186195
'total_time': int(track.get('duration') / 1000),
196+
'episode_art_url' : artwork_url,
187197
}
188198

189199
logger.debug('Read %d episodes from %d cached episodes', read_from_cache, len(self.cache['episodes']))

0 commit comments

Comments
 (0)