all repos

rss-tools @ 05391f1ceeba6d6f8ff2adc080b7e254f645cd6d

get rss feed from sources that(i need and) dont provide one
2 files changed, 65 insertions(+), 14 deletions(-)
musicfeed: include spotify and youtube links
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-06-26 11:35:25 +0300
Authored at: 2026-06-26 11:26:58 +0300
Change ID: xxwqyqrxpklruoswtoyykvsxmpyltwlo
Parent: 58cc1bd
M sources/musicfeed/musicbrainz.go
···
        32
        32
         	CoverArtArchive struct {

      
        33
        33
         		Artwork bool `json:"artwork"`

      
        34
        34
         	} `json:"cover-art-archive"`

      
        
        35
        +	Relations []mbRelation `json:"relations,omitempty"`

      
        
        36
        +}

      
        
        37
        +

      
        
        38
        +type mbRelation struct {

      
        
        39
        +	Type      string `json:"type"`

      
        
        40
        +	Direction string `json:"direction"`

      
        
        41
        +	URL       struct {

      
        
        42
        +		ID       string `json:"id"`

      
        
        43
        +		Resource string `json:"resource"`

      
        
        44
        +	} `json:"url"`

      
        35
        45
         }

      
        36
        46
         

      
        37
        47
         type mbReleaseResponse struct {

      ···
        150
        160
         

      
        151
        161
         func (a *musicbrainzAPI) fetchReleases(ctx context.Context, mbid string) ([]mbRelease, error) {

      
        152
        162
         	u := fmt.Sprintf(

      
        153
        
        -		"%s/release?artist=%s&inc=artist-credits+release-groups&limit=100&fmt=json",

      
        
        163
        +		"%s/release?artist=%s&inc=artist-credits+release-groups+url-rels&limit=100&fmt=json",

      
        154
        164
         		mbBaseURL, mbid,

      
        155
        165
         	)

      
        156
        166
         	resp, err := a.doRequest(ctx, u)

      
M sources/musicfeed/musicfeed.go
···
        31
        31
         	artistName     string

      
        32
        32
         	label          string

      
        33
        33
         	hasArtwork     bool

      
        
        34
        +	spotifyURL     string

      
        
        35
        +	youtubeURL     string

      
        34
        36
         }

      
        35
        37
         

      
        36
        38
         type releaseFetcher interface {

      ···
        188
        190
         	sem := make(chan struct{}, 5)

      
        189
        191
         

      
        190
        192
         	for _, raw := range mf.artists {

      
        191
        
        -		raw := raw

      
        192
        193
         		wg.Add(1)

      
        193
        194
         		sem <- struct{}{}

      
        194
        195
         

      ···
        225
        226
         				if len(r.ArtistCredit) > 0 {

      
        226
        227
         					artistName = r.ArtistCredit[0].Name

      
        227
        228
         				}

      
        
        229
        +				spotifyURL, youtubeURL := extractExtURLs(r.Relations)

      
        228
        230
         				artistReleases = append(artistReleases, release{

      
        229
        231
         					id:             r.ID,

      
        230
        232
         					releaseGroupID: r.ReleaseGroup.ID,

      ···
        234
        236
         					artistName:     artistName,

      
        235
        237
         					label:          label,

      
        236
        238
         					hasArtwork:     r.CoverArtArchive.Artwork,

      
        
        239
        +					spotifyURL:     spotifyURL,

      
        
        240
        +					youtubeURL:     youtubeURL,

      
        237
        241
         				})

      
        238
        242
         			}

      
        239
        243
         

      ···
        282
        286
         	}

      
        283
        287
         

      
        284
        288
         	if isMBID(entry.label) {

      
        285
        
        -		name, err := mf.api.fetchArtist(ctx, entry.label)

      
        286
        
        -		if err != nil {

      
        287
        
        -			mf.logger.Warn("failed to fetch artist name", "mbid", entry.label, "err", err)

      
        
        289
        +		name, ferr := mf.api.fetchArtist(ctx, entry.label)

      
        
        290
        +		if ferr != nil {

      
        
        291
        +			mf.logger.Warn("failed to fetch artist name", "mbid", entry.label, "err", ferr)

      
        288
        292
         			return entry.label, entry.label

      
        289
        293
         		}

      
        290
        
        -		if err := mf.bucket.Set([]byte("mapping:"+name), []byte(entry.label)); err != nil {

      
        291
        
        -			mf.logger.Warn("failed to cache artist mapping", "err", err)

      
        
        294
        +		if berr := mf.bucket.Set([]byte("mapping:"+name), []byte(entry.label)); berr != nil {

      
        
        295
        +			mf.logger.Warn("failed to cache artist mapping", "err", berr)

      
        292
        296
         		}

      
        293
        297
         		return entry.label, name

      
        294
        298
         	}

      ···
        363
        367
         	return time.Time{}

      
        364
        368
         }

      
        365
        369
         

      
        
        370
        +func extractExtURLs(rels []mbRelation) (spotifyURL, youtubeURL string) {

      
        
        371
        +	for _, rel := range rels {

      
        
        372
        +		if rel.Direction != "forward" {

      
        
        373
        +			continue

      
        
        374
        +		}

      
        
        375
        +		resource := rel.URL.Resource

      
        
        376
        +		switch {

      
        
        377
        +		case strings.Contains(resource, "open.spotify.com"):

      
        
        378
        +			spotifyURL = resource

      
        
        379
        +		case strings.Contains(resource, "youtube.com") || strings.Contains(resource, "youtu.be"):

      
        
        380
        +			youtubeURL = resource

      
        
        381
        +		}

      
        
        382
        +	}

      
        
        383
        +	return

      
        
        384
        +}

      
        
        385
        +

      
        366
        386
         func generateFeed(releases []release) *atom.Feed {

      
        367
        387
         	feed := atom.NewFeed("New Music Releases", "musicfeed")

      
        368
        388
         	for _, r := range releases {

      ···
        371
        391
         			displayName = r.artistName

      
        372
        392
         		}

      
        373
        393
         

      
        374
        
        -		links := []atom.Link{

      
        375
        
        -			{

      
        376
        
        -				Rel:  "alternate",

      
        377
        
        -				Href: fmt.Sprintf("https://musicbrainz.org/release/%s", r.id),

      
        378
        
        -			},

      
        379
        
        -		}

      
        
        394
        +		links := []atom.Link{{

      
        
        395
        +			Rel:  "alternate",

      
        
        396
        +			Href: fmt.Sprintf("https://musicbrainz.org/release/%s", r.id),

      
        
        397
        +		}}

      
        380
        398
         

      
        381
        399
         		content, contentType := releaseContent(r, displayName)

      
        382
        400
         

      ···
        388
        406
         			})

      
        389
        407
         		}

      
        390
        408
         

      
        
        409
        +		if r.spotifyURL != "" {

      
        
        410
        +			links = append(links, atom.Link{

      
        
        411
        +				Rel:   "related",

      
        
        412
        +				Href:  r.spotifyURL,

      
        
        413
        +				Title: "Spotify",

      
        
        414
        +			})

      
        
        415
        +		}

      
        
        416
        +		if r.youtubeURL != "" {

      
        
        417
        +			links = append(links, atom.Link{

      
        
        418
        +				Rel:   "related",

      
        
        419
        +				Href:  r.youtubeURL,

      
        
        420
        +				Title: "YouTube",

      
        
        421
        +			})

      
        
        422
        +		}

      
        
        423
        +

      
        391
        424
         		releaseType := strings.TrimSpace(r.releaseType)

      
        392
        425
         		title := fmt.Sprintf("%s — %s", displayName, r.title)

      
        393
        426
         		if releaseType != "" {

      ···
        415
        448
         	}

      
        416
        449
         

      
        417
        450
         	imageURL := fmt.Sprintf("%s/release/%s/front-250.jpg", caaBaseURL, r.id)

      
        418
        
        -	parts := make([]string, 0, 4)

      
        
        451
        +	parts := make([]string, 0, 6)

      
        419
        452
         	parts = append(parts, "<body>")

      
        420
        453
         

      
        421
        454
         	releaseType := strings.TrimSpace(r.releaseType)

      ···
        428
        461
         	parts = append(parts, "<p>"+html.EscapeString(text)+"</p>")

      
        429
        462
         	parts = append(parts,

      
        430
        463
         		fmt.Sprintf(`<p><img src="%s" alt="%s"/></p>`, html.EscapeString(imageURL), html.EscapeString(r.title)))

      
        
        464
        +

      
        
        465
        +	if r.spotifyURL != "" {

      
        
        466
        +		parts = append(parts, fmt.Sprintf(`<p><a href="%s">Spotify</a></p>`, html.EscapeString(r.spotifyURL)))

      
        
        467
        +	}

      
        
        468
        +	if r.youtubeURL != "" {

      
        
        469
        +		parts = append(parts, fmt.Sprintf(`<p><a href="%s">YouTube</a></p>`, html.EscapeString(r.youtubeURL)))

      
        
        470
        +	}

      
        
        471
        +

      
        431
        472
         	parts = append(parts, "</body>")

      
        432
        473
         

      
        433
        474
         	return strings.Join(parts, ""), "xhtml"