Merge pull request #530 from dlynas/feat/explicit-badges
feat: add explicit badges to discography modal and artist-detail cards
This commit is contained in:
commit
c77aa61fdf
7 changed files with 40 additions and 5 deletions
|
|
@ -212,6 +212,7 @@ class Album:
|
|||
album_type: str
|
||||
image_url: Optional[str] = None
|
||||
external_urls: Optional[Dict[str, str]] = None
|
||||
explicit: Optional[bool] = None
|
||||
|
||||
@classmethod
|
||||
def from_deezer_album(cls, album_data: Dict[str, Any]) -> 'Album':
|
||||
|
|
@ -243,7 +244,8 @@ class Album:
|
|||
total_tracks=album_data.get('nb_tracks', 0),
|
||||
album_type=album_type,
|
||||
image_url=image_url,
|
||||
external_urls=external_urls if external_urls else None
|
||||
external_urls=external_urls if external_urls else None,
|
||||
explicit=bool(album_data.get('explicit_lyrics', False)),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -565,6 +565,7 @@ class HydrabaseClient:
|
|||
album_type=item_type,
|
||||
image_url=item.get('image_url'),
|
||||
external_urls=ext_urls,
|
||||
explicit=item.get('explicit'),
|
||||
))
|
||||
except Exception as e:
|
||||
logger.debug(f"Skipping malformed Hydrabase artist album: {e}")
|
||||
|
|
|
|||
|
|
@ -167,7 +167,8 @@ class Album:
|
|||
album_type: str
|
||||
image_url: Optional[str] = None
|
||||
external_urls: Optional[Dict[str, str]] = None
|
||||
|
||||
explicit: Optional[bool] = None
|
||||
|
||||
@classmethod
|
||||
def from_itunes_album(cls, album_data: Dict[str, Any]) -> 'Album':
|
||||
# Get highest quality artwork
|
||||
|
|
@ -209,7 +210,8 @@ class Album:
|
|||
total_tracks=track_count,
|
||||
album_type=album_type,
|
||||
image_url=image_url,
|
||||
external_urls=external_urls if external_urls else None
|
||||
external_urls=external_urls if external_urls else None,
|
||||
explicit=album_data.get('collectionExplicitness') == 'explicit',
|
||||
)
|
||||
|
||||
@dataclass
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ def _build_discography_release_dict(release: Any, artist_id: str,
|
|||
'image_url': typed_album.image_url,
|
||||
'total_tracks': typed_album.total_tracks or 0,
|
||||
'external_urls': typed_album.external_urls or {},
|
||||
'explicit': typed_album.explicit,
|
||||
}
|
||||
|
||||
release_id = _extract_lookup_value(release, 'id', 'album_id', 'release_id')
|
||||
|
|
@ -168,6 +169,7 @@ def _build_discography_release_dict(release: Any, artist_id: str,
|
|||
'image_url': _extract_lookup_value(release, 'image_url', 'thumb_url', 'cover_image'),
|
||||
'total_tracks': _extract_lookup_value(release, 'total_tracks', default=0) or 0,
|
||||
'external_urls': _extract_lookup_value(release, 'external_urls', default={}) or {},
|
||||
'explicit': _extract_lookup_value(release, 'explicit'),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -436,6 +438,7 @@ def _build_artist_detail_release_card(release: Dict[str, Any],
|
|||
'track_count': typed_album.total_tracks or 0,
|
||||
'owned': None,
|
||||
'track_completion': 'checking',
|
||||
'explicit': typed_album.explicit,
|
||||
}
|
||||
if typed_album.release_date:
|
||||
card['release_date'] = typed_album.release_date
|
||||
|
|
@ -470,6 +473,7 @@ def _build_artist_detail_release_card(release: Dict[str, Any],
|
|||
'track_count': _extract_lookup_value(release, 'track_count', 'total_tracks', default=0) or 0,
|
||||
'owned': None,
|
||||
'track_completion': 'checking',
|
||||
'explicit': _extract_lookup_value(release, 'explicit'),
|
||||
}
|
||||
|
||||
if release_date:
|
||||
|
|
|
|||
|
|
@ -100,6 +100,8 @@ class Album:
|
|||
label: Optional[str] = None # Record label / publisher
|
||||
barcode: Optional[str] = None # UPC/EAN — Discogs/MusicBrainz only
|
||||
|
||||
explicit: Optional[bool] = None # True=explicit, False=clean, None=unknown
|
||||
|
||||
# Source provenance
|
||||
source: str = '' # 'spotify' / 'itunes' / etc — set by converter
|
||||
external_ids: Dict[str, str] = field(default_factory=dict)
|
||||
|
|
@ -193,6 +195,8 @@ class Album:
|
|||
release_date = release_date.split('T', 1)[0]
|
||||
|
||||
primary_genre = _str(raw.get('primaryGenreName'))
|
||||
ce = _str(raw.get('collectionExplicitness'))
|
||||
explicit = True if ce == 'explicit' else (False if ce in ('notExplicit', 'cleaned') else None)
|
||||
return cls(
|
||||
id=_str(raw.get('collectionId')),
|
||||
name=name,
|
||||
|
|
@ -203,6 +207,7 @@ class Album:
|
|||
image_url=_itunes_artwork(raw.get('artworkUrl100')),
|
||||
artist_id=artist_id,
|
||||
genres=[primary_genre] if primary_genre else [],
|
||||
explicit=explicit,
|
||||
source='itunes',
|
||||
external_ids=external_ids,
|
||||
external_urls=external_urls,
|
||||
|
|
@ -238,6 +243,8 @@ class Album:
|
|||
if raw.get('link'):
|
||||
external_urls['deezer'] = _str(raw['link'])
|
||||
|
||||
_el = raw.get('explicit_lyrics')
|
||||
explicit = bool(_el) if _el is not None else None
|
||||
return cls(
|
||||
id=_str(raw.get('id')),
|
||||
name=_str(raw.get('title')),
|
||||
|
|
@ -251,6 +258,7 @@ class Album:
|
|||
if isinstance(g, dict) and g.get('name')],
|
||||
label=_str(raw.get('label')) or None,
|
||||
barcode=external_ids.get('upc'),
|
||||
explicit=explicit,
|
||||
source='deezer',
|
||||
external_ids=external_ids,
|
||||
external_urls=external_urls,
|
||||
|
|
@ -513,6 +521,8 @@ class Album:
|
|||
if raw.get('soul_id'):
|
||||
external_ids['soul'] = _str(raw['soul_id'])
|
||||
|
||||
_he = raw.get('explicit')
|
||||
explicit = bool(_he) if _he is not None else None
|
||||
return cls(
|
||||
id=_str(raw.get('id')),
|
||||
name=_str(raw.get('name') or raw.get('title')),
|
||||
|
|
@ -522,6 +532,7 @@ class Album:
|
|||
album_type=_str(raw.get('album_type'), default='album'),
|
||||
image_url=_str(raw.get('image_url') or raw.get('thumb_url')) or None,
|
||||
artist_id=_str(raw.get('artist_id')) or None,
|
||||
explicit=explicit,
|
||||
source='hydrabase',
|
||||
external_ids=external_ids,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1859,7 +1859,7 @@ function createReleaseCard(release) {
|
|||
content.className = "album-card-content";
|
||||
const _esc = (s) => String(s || '').replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
||||
content.innerHTML = `
|
||||
<div class="album-card-name" title="${_esc(release.title)}">${_esc(release.title)}</div>
|
||||
<div class="album-card-name" title="${_esc(release.title)}">${_esc(release.title)}${release.explicit === true ? ' <span class="explicit-badge">E</span>' : ''}</div>
|
||||
${yearText ? `<div class="album-card-year">${_esc(yearText)}</div>` : ''}
|
||||
`;
|
||||
card.appendChild(content);
|
||||
|
|
@ -2507,7 +2507,7 @@ function _renderDiscogCard(release, index, completionData) {
|
|||
${statusIcon ? `<span class="discog-card-status">${statusIcon}</span>` : ''}
|
||||
</div>
|
||||
<div class="discog-card-info">
|
||||
<div class="discog-card-title">${_esc(albumName)}</div>
|
||||
<div class="discog-card-title">${_esc(albumName)}${release.explicit === true ? ' <span class="explicit-badge">E</span>' : ''}</div>
|
||||
<div class="discog-card-meta">${year}${year && tracks ? ' · ' : ''}${tracks ? tracks + ' tracks' : ''}</div>
|
||||
</div>
|
||||
<div class="discog-card-check"></div>
|
||||
|
|
|
|||
|
|
@ -45985,6 +45985,21 @@ textarea.enhanced-meta-field-input {
|
|||
|
||||
.discog-card-meta { font-size: 11px; color: rgba(255,255,255,0.35); margin-top: 2px; }
|
||||
|
||||
.explicit-badge {
|
||||
display: inline-block; font-size: 9px; font-weight: 800; letter-spacing: 0.04em;
|
||||
padding: 1px 4px; border-radius: 3px; vertical-align: middle; margin-left: 5px;
|
||||
background: rgba(255,255,255,0.12); color: rgba(255,255,255,0.55);
|
||||
border: 1px solid rgba(255,255,255,0.15); line-height: 1.4; flex-shrink: 0;
|
||||
font-family: inherit;
|
||||
}
|
||||
.album-card-explicit {
|
||||
display: inline-block; font-size: 9px; font-weight: 800; letter-spacing: 0.04em;
|
||||
padding: 1px 4px; border-radius: 3px; vertical-align: middle; margin-left: 4px;
|
||||
background: rgba(255,255,255,0.12); color: rgba(255,255,255,0.55);
|
||||
border: 1px solid rgba(255,255,255,0.2); line-height: 1.4;
|
||||
font-family: inherit; text-shadow: none;
|
||||
}
|
||||
|
||||
.discog-card-check {
|
||||
width: 20px; height: 20px; border-radius: 6px; flex-shrink: 0;
|
||||
border: 2px solid rgba(255,255,255,0.15); transition: all 0.15s ease;
|
||||
|
|
|
|||
Loading…
Reference in a new issue