Skip to content

Commit

Permalink
Merge pull request #21 from tphoney/music_filtering
Browse files Browse the repository at this point in the history
Music filtering
  • Loading branch information
tphoney authored May 5, 2024
2 parents 6de6a22 + 9f8aa37 commit 5e76834
Show file tree
Hide file tree
Showing 8 changed files with 450 additions and 13 deletions.
1 change: 1 addition & 0 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
- add a search provider mechanism for artists/albums
- add ui for music search
- spotify search
- bastille is showing "give me the future" as an album. wrong !!
4 changes: 3 additions & 1 deletion spotify/spotify.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,13 @@ func SearchSpotifyAlbums(artistID, clientID, clientSecret string) (albums []type
_ = json.Unmarshal(body, &albumsResponse)

for i := range albumsResponse.Items {
// convert "2022-06-03" to "2022"
year := strings.Split(albumsResponse.Items[i].ReleaseDate, "-")[0]
albums = append(albums, types.MusicSearchAlbumResult{
Title: albumsResponse.Items[i].Name,
ID: albumsResponse.Items[i].ID,
URL: albumsResponse.Items[i].ExternalUrls.Spotify,
Year: albumsResponse.Items[i].ReleaseDate,
Year: year,
})
}

Expand Down
4 changes: 2 additions & 2 deletions spotify/spotify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func TestSearchSpotifyArtistDebug(t *testing.T) {
if spotifyClientID == "" || spotifyClientSecret == "" {
t.Skip("SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET not set")
}
artist := &types.PlexMusicArtist{Name: "Aaliyah"}
artist := &types.PlexMusicArtist{Name: "Angel Olsen"}
artistSearchResult, err := SearchSpotifyArtist(artist, spotifyClientID, spotifyClientSecret)
if err != nil {
t.Errorf("SearchSpotifyArtist() error = %v", err)
Expand All @@ -88,7 +88,7 @@ func TestSearchSpotifyAlbumsDebug(t *testing.T) {
if spotifyClientID == "" || spotifyClientSecret == "" {
t.Skip("SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET not set")
}
artistID := "0urTpYCsixqZwgNTkPJOJ4"
artistID := "6mKqFxGMS5TGDZI3XkT5Rt"
artistSearchResult, err := SearchSpotifyAlbums(artistID, spotifyClientID, spotifyClientSecret)
if err != nil {
t.Errorf("SearchSpotifyAlbum() error = %v", err)
Expand Down
35 changes: 30 additions & 5 deletions utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package utils

import (
"regexp"
"slices"
"strconv"
"strings"
"time"

"github.com/tphoney/plex-lookup/types"
Expand All @@ -13,8 +15,7 @@ func MarkBestMatch(search *types.SearchResults) types.SearchResults {
for i := range search.MovieSearchResults {
// normally a match if the year is within 1 year of each other
resultYear := YearToDate(search.MovieSearchResults[i].Year)
if search.MovieSearchResults[i].FoundTitle == search.PlexMovie.Title && (resultYear.Year() == expectedYear.Year() ||
resultYear.Year() == expectedYear.Year()-1 || resultYear.Year() == expectedYear.Year()+1) {
if search.MovieSearchResults[i].FoundTitle == search.PlexMovie.Title && WitinOneYear(resultYear.Year(), expectedYear.Year()) {
search.MovieSearchResults[i].BestMatch = true
if search.MovieSearchResults[i].Format == types.DiskBluray {
search.MatchesBluray++
Expand All @@ -26,9 +27,7 @@ func MarkBestMatch(search *types.SearchResults) types.SearchResults {
}
for i := range search.TVSearchResults {
resultYear := YearToDate(search.TVSearchResults[i].Year)
if search.TVSearchResults[i].FoundTitle == search.PlexTVShow.Title &&
// normally a match if the year is within 1 year of each other
(resultYear.Year() == expectedYear.Year() || resultYear.Year() == expectedYear.Year()-1 || resultYear.Year() == expectedYear.Year()+1) {
if search.TVSearchResults[i].FoundTitle == search.PlexTVShow.Title && WitinOneYear(resultYear.Year(), expectedYear.Year()) {
search.TVSearchResults[i].BestMatch = true
if slices.Contains(search.TVSearchResults[i].Format, types.DiskBluray) {
search.MatchesBluray++
Expand All @@ -48,3 +47,29 @@ func YearToDate(yearString string) time.Time {
}
return time.Date(year, 1, 1, 0, 0, 0, 0, time.UTC)
}

func CompareTitles(title1, title2 string) bool {
// remove anything between ()
r := regexp.MustCompile(`\((.*?)\)`)
title1 = r.ReplaceAllString(title1, "")
title2 = r.ReplaceAllString(title2, "")
// remove anything between []
r = regexp.MustCompile(`\[(.*?)\]`)
title1 = r.ReplaceAllString(title1, "")
title2 = r.ReplaceAllString(title2, "")
// remove anything between {}
r = regexp.MustCompile(`\{(.*?)\}`)
title1 = r.ReplaceAllString(title1, "")
title2 = r.ReplaceAllString(title2, "")
// strip whitespace
title1 = strings.TrimSpace(title1)
title2 = strings.TrimSpace(title2)
// lowercase
title1 = strings.ToLower(title1)
title2 = strings.ToLower(title2)
return title1 == title2
}

func WitinOneYear(year1, year2 int) bool {
return year1 == year2 || year1 == year2-1 || year1 == year2+1
}
95 changes: 95 additions & 0 deletions utils/utils_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package utils

import (
"fmt"
"testing"
"time"

Expand Down Expand Up @@ -116,3 +117,97 @@ func TestMarkBestMatch(t *testing.T) {
}
}
}

func Test_albumTitlesMatch(t *testing.T) {
tests := []struct {
title1 string
title2 string
want bool
}{
{
title1: "Test Album",
title2: "Test Album",
want: true,
},
{
title1: "Test Album (Deluxe Edition)",
title2: "Test Album",
want: true,
},
{
title1: "Test Album [Remastered]",
title2: "Test Album",
want: true,
},
{
title1: "Test Album {Special Edition}",
title2: "Test Album",
want: true,
},
{
title1: "Test Album (Deluxe Edition) [Remastered] {Special Edition}",
title2: "Test Album",
want: true,
},
{
title1: "Test Album (Live)",
title2: "Test Album (Studio)",
want: true,
},
{
title1: "Test Album [Remastered]",
title2: "Test Album2 [Deluxe Edition]",
want: false,
},
// test for case insensitivity
{
title1: "Test Album",
title2: "test album",
want: true,
},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("title1=%s, title2=%s", tt.title1, tt.title2), func(t *testing.T) {
if got := CompareTitles(tt.title1, tt.title2); got != tt.want {
t.Errorf("albumTitlesMatch() = %v, want %v", got, tt.want)
}
})
}
}
func TestWitinOneYear(t *testing.T) {
// Test case 1: Same year
year1 := 2022
year2 := 2022
expectedResult := true
result := WitinOneYear(year1, year2)
if result != expectedResult {
t.Errorf("Expected %v, but got %v", expectedResult, result)
}

// Test case 2: Year difference of 1
year1 = 2022
year2 = 2021
expectedResult = true
result = WitinOneYear(year1, year2)
if result != expectedResult {
t.Errorf("Expected %v, but got %v", expectedResult, result)
}

// Test case 3: Year difference of -1
year1 = 2022
year2 = 2023
expectedResult = true
result = WitinOneYear(year1, year2)
if result != expectedResult {
t.Errorf("Expected %v, but got %v", expectedResult, result)
}

// Test case 4: Year difference greater than 1
year1 = 2022
year2 = 2020
expectedResult = false
result = WitinOneYear(year1, year2)
if result != expectedResult {
t.Errorf("Expected %v, but got %v", expectedResult, result)
}
}
73 changes: 69 additions & 4 deletions web/music.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import (
"fmt"
"html/template"
"net/http"
"strconv"
"time"

"github.com/tphoney/plex-lookup/musicbrainz"
"github.com/tphoney/plex-lookup/plex"
"github.com/tphoney/plex-lookup/spotify"
"github.com/tphoney/plex-lookup/types"
"github.com/tphoney/plex-lookup/utils"
)

var (
Expand All @@ -22,6 +24,7 @@ var (
totalArtists int = 0
plexMusic []types.PlexMusicArtist
artistsSearchResults []types.SearchResults
albumReleaseYearCutoff int = 2
)

func musicHandler(w http.ResponseWriter, _ *http.Request) {
Expand All @@ -44,7 +47,7 @@ func processArtistHTML(w http.ResponseWriter, r *http.Request) {
var searchResult types.SearchResults
artistsJobRunning = true
numberOfArtistsProcessed = 0
totalArtists = 49 // len(plexMusic) - 1
totalArtists = len(plexMusic) - 1

fmt.Fprintf(w, `<div hx-get="/progressartists" hx-trigger="every 100ms" class="container" id="progress">
<progress value="%d" max= "%d"/></div>`, numberOfArtistsProcessed, totalArtists)
Expand All @@ -67,7 +70,7 @@ func processArtistHTML(w http.ResponseWriter, r *http.Request) {
// search spotify
go func() {
startTime := time.Now()
for i := 0; i < 50; i++ {
for i := range plexMusic {
fmt.Print(".")
searchResult, _ = spotify.SearchSpotifyArtist(&plexMusic[i], config.SpotifyClientID, config.SpotifyClientSecret)
artistsSearchResults = append(artistsSearchResults, searchResult)
Expand Down Expand Up @@ -104,15 +107,16 @@ func artistProgressBarHTML(w http.ResponseWriter, _ *http.Request) {
}

func renderArtistsTable(searchResults []types.SearchResults) (tableRows string) {
searchResults = filterMusicSearchResults(searchResults)
tableRows = `<thead><tr><th data-sort="string"><strong>Plex Artist</strong></th><th data-sort="int"><strong>Albums</strong></th><th><strong>Album</strong></th></tr></thead><tbody>` //nolint: lll
for i := range searchResults {
if len(searchResults[i].MusicSearchResults) > 0 {
tableRows += fmt.Sprintf("<tr><td><a href='%s'>%s</a></td><td>%d</td><td><ul>",
tableRows += fmt.Sprintf("<tr><td><a href=%q>%s</a></td><td>%d</td><td><ul>",
searchResults[i].MusicSearchResults[0].URL,
searchResults[i].PlexMusicArtist.Name,
len(searchResults[i].MusicSearchResults[0].Albums))
for j := range searchResults[i].MusicSearchResults[0].Albums {
tableRows += fmt.Sprintf("<li><a href='%s'>%s</a> (%s)</li>",
tableRows += fmt.Sprintf("<li><a href=%q>%s</a> (%s)</li>",
searchResults[i].MusicSearchResults[0].Albums[j].URL,
searchResults[i].MusicSearchResults[0].Albums[j].Title,
searchResults[i].MusicSearchResults[0].Albums[j].Year)
Expand All @@ -122,3 +126,64 @@ func renderArtistsTable(searchResults []types.SearchResults) (tableRows string)
}
return tableRows // Return the generated HTML for table rows
}

func filterMusicSearchResults(searchResults []types.SearchResults) []types.SearchResults {
searchResults = removeOwnedAlbums(searchResults)
searchResults = removeOlderSearchedAlbums(searchResults)
return searchResults
}

func removeOlderSearchedAlbums(searchResults []types.SearchResults) []types.SearchResults {
cutoffYear := time.Now().Year() - albumReleaseYearCutoff
filteredResults := make([]types.SearchResults, 0)
for i := range searchResults {
if len(searchResults[i].MusicSearchResults) > 0 {
filteredAlbums := make([]types.MusicSearchAlbumResult, 0)
for _, album := range searchResults[i].MusicSearchResults[0].Albums {
albumYear, _ := strconv.Atoi(album.Year)
if albumYear >= cutoffYear {
filteredAlbums = append(filteredAlbums, album)
}
}
searchResults[i].MusicSearchResults[0].Albums = filteredAlbums
filteredResults = append(filteredResults, searchResults[i])
}
}
return filteredResults
}

func removeOwnedAlbums(searchResults []types.SearchResults) []types.SearchResults {
for i := range searchResults {
if len(searchResults[i].MusicSearchResults) > 0 {
albumsToRemove := make([]types.MusicSearchAlbumResult, 0)
// iterate over plex albums
for _, plexAlbum := range searchResults[i].PlexMusicArtist.Albums {
// iterate over search results
for _, album := range searchResults[i].MusicSearchResults[0].Albums {
if utils.CompareTitles(plexAlbum.Title, album.Title) {
albumsToRemove = append(albumsToRemove, album)
}
}
}
searchResults[i].MusicSearchResults[0].Albums = cleanAlbums(searchResults[i].MusicSearchResults[0].Albums, albumsToRemove)
}
}
return searchResults
}

func cleanAlbums(original, toRemove []types.MusicSearchAlbumResult) []types.MusicSearchAlbumResult {
cleaned := make([]types.MusicSearchAlbumResult, 0)
for _, album := range original {
found := false
for _, remove := range toRemove {
if album.Title == remove.Title && album.Year == remove.Year {
found = true
break
}
}
if !found {
cleaned = append(cleaned, album)
}
}
return cleaned
}
2 changes: 1 addition & 1 deletion web/music.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ <h1 class="container">Music</h1>
<legend><strong>Lookup Filters:</strong></legend>
<label for="Missing albums">
<input type="radio" id="missingalbums" name="lookuptype" value="missingalbums" checked />
Missing albums
Missing albums (last 2 years)
</label>
<label for="New artists">
<input type="radio" id="newartists" name="lookuptype" value="newartists" disabled="true" />
Expand Down
Loading

0 comments on commit 5e76834

Please sign in to comment.