Skip to content

Commit

Permalink
Merge pull request #22 from tphoney/tv_filtering
Browse files Browse the repository at this point in the history
(fix) improve flawed tv filtering
  • Loading branch information
tphoney authored May 7, 2024
2 parents 5e76834 + ef165e8 commit 484b027
Show file tree
Hide file tree
Showing 14 changed files with 359 additions and 142 deletions.
8 changes: 5 additions & 3 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

## bugs

- add plex name hover over for tv/movies/music, so we can identify if there is a mismatch between the plex name and the search name
- allow amazon tv search for newer series
- update movies to use tv like search
- write a function to calculate plex dates
- allow amazon tv search for indivdual series
- remove links to tv series we already have in plex. eg dont show adventure time series 1 and 2 ?
- remove dead fields from the tv data types

## done

Expand All @@ -26,3 +26,5 @@
- add ui for music search
- spotify search
- bastille is showing "give me the future" as an album. wrong !!
- remove dead fields from the tv data types
- remove links to tv series we already have in plex. eg dont show adventure time series 1 and 2 ?
30 changes: 15 additions & 15 deletions amazon/amazon.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ const (
amazonURL = "https://www.blu-ray.com/movies/search.php?keyword="
)

func ScrapeMovies(movieSearchResult *types.SearchResults) (scrapedResults []types.MovieSearchResult) {
func ScrapeTitles(searchResults *types.SearchResults) (scrapedResults []types.MovieSearchResult) {
var results, lookups []types.MovieSearchResult
for _, searchResult := range movieSearchResult.MovieSearchResults {
for _, searchResult := range searchResults.MovieSearchResults {
if !searchResult.BestMatch {
results = append(results, searchResult)
} else {
Expand All @@ -37,7 +37,7 @@ func ScrapeMovies(movieSearchResult *types.SearchResults) (scrapedResults []type
go func() {
semaphore <- struct{}{}
defer func() { <-semaphore }()
scrapeMovie(&lookups[i], movieSearchResult.PlexMovie.DateAdded, ch)
scrapeTitle(&lookups[i], searchResults.PlexMovie.DateAdded, ch)
}()
}

Expand All @@ -49,7 +49,7 @@ func ScrapeMovies(movieSearchResult *types.SearchResults) (scrapedResults []type
return results
}

func scrapeMovie(movie *types.MovieSearchResult, dateAdded time.Time, ch chan<- *types.MovieSearchResult) {
func scrapeTitle(movie *types.MovieSearchResult, dateAdded time.Time, ch chan<- *types.MovieSearchResult) {
req, err := http.NewRequestWithContext(context.Background(), "GET", movie.URL, bytes.NewBuffer([]byte{}))
movie.ReleaseDate = time.Time{}
if err != nil {
Expand Down Expand Up @@ -78,14 +78,14 @@ func scrapeMovie(movie *types.MovieSearchResult, dateAdded time.Time, ch chan<-
return
}
rawData := string(body)
movie.ReleaseDate = findMovieDetails(rawData)
movie.ReleaseDate = findTitleDetails(rawData)
if movie.ReleaseDate.After(dateAdded) {
movie.NewRelease = true
}
ch <- movie
}

func findMovieDetails(response string) (releaseDate time.Time) {
func findTitleDetails(response string) (releaseDate time.Time) {
r := regexp.MustCompile(`<a class="grey noline" alt=".*">(.*?)</a></span>`)

match := r.FindStringSubmatch(response)
Expand Down Expand Up @@ -196,7 +196,7 @@ func SearchAmazonTV(plexTVShow *types.PlexTVShow, filter string) (tvSearchResult
}

func findTitlesInResponse(response string, movie bool) (movieResults []types.MovieSearchResult, tvResults []types.TVSearchResult) {
// Find the start and end index of the movie entry
// Find the start and end index of the entry
for {
startIndex := strings.Index(response, `<a class="hoverlink" data-globalproductid=`)
// remove everything before the start index
Expand All @@ -208,16 +208,16 @@ func findTitlesInResponse(response string, movie bool) (movieResults []types.Mov

// If both start and end index are found
if endIndex != -1 {
// Extract the movie entry
movieEntry := response[0:endIndex]
// Find the URL of the movie
urlStartIndex := strings.Index(movieEntry, "href=\"") + len("href=\"")
urlEndIndex := strings.Index(movieEntry[urlStartIndex:], "\"") + urlStartIndex
returnURL := movieEntry[urlStartIndex:urlEndIndex]
// Find the title of the movie
// Extract the entry
entry := response[0:endIndex]
// Find the URL
urlStartIndex := strings.Index(entry, "href=\"") + len("href=\"")
urlEndIndex := strings.Index(entry[urlStartIndex:], "\"") + urlStartIndex
returnURL := entry[urlStartIndex:urlEndIndex]
// Find the title
r := regexp.MustCompile(`title="(.*?)\s*\((.*?)\)"`)
// Find the first match
match := r.FindStringSubmatch(movieEntry)
match := r.FindStringSubmatch(entry)

if match != nil {
// Extract and print title and year
Expand Down
2 changes: 1 addition & 1 deletion amazon/amazon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestFindMovieDetails(t *testing.T) {
t.Errorf("Error reading testdata/anchorman.html: %s", err)
}

processed := findMovieDetails(string(rawdata))
processed := findTitleDetails(string(rawdata))
expected := time.Date(2010, time.October, 4, 0, 0, 0, 0, time.UTC)
if processed.Compare(expected) != 0 {
t.Errorf("Expected %s, but got %s", expected, processed)
Expand Down
14 changes: 7 additions & 7 deletions cinemaparadiso/cinemaparadiso.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ func SearchCinemaParadisoTV(plexTVShow *types.PlexTVShow) (tvSearchResult types.
// now we can get the series information for each best match
for i := range tvSearchResult.TVSearchResults {
if tvSearchResult.TVSearchResults[i].BestMatch {
tvSearchResult.TVSearchResults[i].Series, _ = findTVSeriesInfo(tvSearchResult.TVSearchResults[i].URL)
tvSearchResult.TVSearchResults[i].Seasons, _ = findTVSeriesInfo(tvSearchResult.TVSearchResults[i].URL)
}
}
return tvSearchResult, nil
}

func findTVSeriesInfo(seriesURL string) (tvSeries []types.TVSeriesResult, err error) {
func findTVSeriesInfo(seriesURL string) (tvSeries []types.TVSeasonResult, err error) {
// make a request to the url
req, err := http.NewRequestWithContext(context.Background(), "GET", seriesURL, bytes.NewBuffer([]byte{}))
if err != nil {
Expand Down Expand Up @@ -118,18 +118,18 @@ func makeSearchRequest(urlEncodedTitle string) (rawResponse string, err error) {
return rawData, nil
}

func findTVSeriesInResponse(response string) (tvSeries []types.TVSeriesResult) {
func findTVSeriesInResponse(response string) (tvSeries []types.TVSeasonResult) {
// look for the series in the response
r := regexp.MustCompile(`<li data-filmId="(\d*)">`)
match := r.FindAllStringSubmatch(response, -1)
for i, m := range match {
tvSeries = append(tvSeries, types.TVSeriesResult{Number: i, URL: m[1]})
tvSeries = append(tvSeries, types.TVSeasonResult{Number: i, URL: m[1]})
}
// remove the first entry as it is general information
results := make([]types.TVSeriesResult, 0, len(tvSeries))
results := make([]types.TVSeasonResult, 0, len(tvSeries))
if len(tvSeries) > 0 {
tvSeries = tvSeries[1:]
ch := make(chan *types.TVSeriesResult, len(tvSeries))
ch := make(chan *types.TVSeasonResult, len(tvSeries))

semaphore := make(chan struct{}, types.ConcurrencyLimit)
for i := range tvSeries {
Expand All @@ -152,7 +152,7 @@ func findTVSeriesInResponse(response string) (tvSeries []types.TVSeriesResult) {
return results
}

func makeSeriesRequest(tv types.TVSeriesResult, ch chan<- *types.TVSeriesResult) {
func makeSeriesRequest(tv types.TVSeasonResult, ch chan<- *types.TVSeasonResult) {
content := []byte(fmt.Sprintf("FilmID=%s", tv.URL))
req, err := http.NewRequestWithContext(context.Background(), "POST", cinemaparadisoSeriesURL, bytes.NewBuffer(content))
if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ func initializeFlags() {

func initializePlexMovies() []types.PlexMovie {
var allMovies []types.PlexMovie
allMovies = append(allMovies, plex.GetPlexMovies(plexIP, plexMovieLibraryID, plexToken, "sd", nil)...)
allMovies = append(allMovies, plex.GetPlexMovies(plexIP, plexMovieLibraryID, plexToken, "480", nil)...)
allMovies = append(allMovies, plex.GetPlexMovies(plexIP, plexMovieLibraryID, plexToken, "576", nil)...)
allMovies = append(allMovies, plex.GetPlexMovies(plexIP, plexMovieLibraryID, plexToken, "720", nil)...)
allMovies = append(allMovies, plex.GetPlexMovies(plexIP, plexMovieLibraryID, plexToken, types.PlexResolution240, nil)...)
allMovies = append(allMovies, plex.GetPlexMovies(plexIP, plexMovieLibraryID, plexToken, types.PlexResolution480, nil)...)
allMovies = append(allMovies, plex.GetPlexMovies(plexIP, plexMovieLibraryID, plexToken, types.PlexResolution576, nil)...)
allMovies = append(allMovies, plex.GetPlexMovies(plexIP, plexMovieLibraryID, plexToken, types.PlexResolution720, nil)...)

fmt.Printf("\nThere are a total of %d movies in the library.\n\nMovies available:\n", len(allMovies))
return allMovies
Expand Down
2 changes: 1 addition & 1 deletion musicbrainz/musicbrainz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestSearchMusicBrainzArtist(t *testing.T) {
{
Name: "AC/DC",
ID: "66c662b6-6e2f-4930-8610-912e24c63ed1",
Albums: make([]types.MusicSearchAlbumResult, 17),
Albums: make([]types.MusicSearchAlbumResult, 18),
},
},
},
Expand Down
69 changes: 51 additions & 18 deletions plex/plex.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ func GetPlexMovies(ipAddress, libraryID, plexToken, resolution string, filters [
return movieList
}

func GetPlexTV(ipAddress, libraryID, plexToken string, resolutions []string) (tvShowList []types.PlexTVShow) {
func GetPlexTV(ipAddress, libraryID, plexToken string) (tvShowList []types.PlexTVShow) {
url := fmt.Sprintf("http://%s:32400/library/sections/%s/all", ipAddress, libraryID)

req, err := http.NewRequestWithContext(context.Background(), "GET", url, http.NoBody)
Expand Down Expand Up @@ -547,7 +547,7 @@ func GetPlexTV(ipAddress, libraryID, plexToken string, resolutions []string) (tv
tvShowList = extractTVShows(string(body))
// now we need to get the episodes for each TV show
for i := range tvShowList {
tvShowList[i].Seasons = GetPlexTVSeasons(ipAddress, plexToken, tvShowList[i].RatingKey, resolutions)
tvShowList[i].Seasons = GetPlexTVSeasons(ipAddress, plexToken, tvShowList[i].RatingKey)
}
// remove TV shows with no seasons
var filteredTVShows []types.PlexTVShow
Expand Down Expand Up @@ -677,7 +677,7 @@ func extractMusicArtists(xmlString string) (artists []types.PlexMusicArtist, err
return artists, nil
}

func GetPlexTVSeasons(ipAddress, plexToken, ratingKey string, resolutions []string) (seasonList []types.PlexTVSeason) {
func GetPlexTVSeasons(ipAddress, plexToken, ratingKey string) (seasonList []types.PlexTVSeason) {
url := fmt.Sprintf("http://%s:32400/library/metadata/%s/children?", ipAddress, ratingKey)

req, err := http.NewRequestWithContext(context.Background(), "GET", url, http.NoBody)
Expand Down Expand Up @@ -707,22 +707,57 @@ func GetPlexTVSeasons(ipAddress, plexToken, ratingKey string, resolutions []stri
// os.WriteFile("seasons.xml", body, 0644)
// now we need to get the episodes for each TV show
for i := range seasonList {
episodes := GetPlexTVEpisodes(ipAddress, plexToken, seasonList[i].RatingKey, resolutions)
episodes := GetPlexTVEpisodes(ipAddress, plexToken, seasonList[i].RatingKey)
if len(episodes) > 0 {
seasonList[i].Episodes = episodes
}
}
// remove seasons with no episodes
var filteredSeasons []types.PlexTVSeason
for i := range seasonList {
if len(seasonList[i].Episodes) > 0 {
filteredSeasons = append(filteredSeasons, seasonList[i])
if len(seasonList[i].Episodes) < 1 {
continue
}
// lets add all of the resolutions for the episodes
var listOfResolutions []string
for j := range seasonList[i].Episodes {
listOfResolutions = append(listOfResolutions, seasonList[i].Episodes[j].Resolution)
}
// now we have all of the resolutions for the episodes
seasonList[i].LowestResolution = findLowestResolution(listOfResolutions)
// get the last episode added date
seasonList[i].LastEpisodeAdded = seasonList[i].Episodes[len(seasonList[i].Episodes)-1].DateAdded
filteredSeasons = append(filteredSeasons, seasonList[i])
}
return filteredSeasons
}

func GetPlexTVEpisodes(ipAddress, plexToken, ratingKey string, resolutions []string) (episodeList []types.PlexTVEpisode) {
func findLowestResolution(resolutions []string) (lowestResolution string) {
if slices.Contains(resolutions, types.PlexResolutionSD) {
return types.PlexResolutionSD
}
if slices.Contains(resolutions, types.PlexResolution240) {
return types.PlexResolution240
}
if slices.Contains(resolutions, types.PlexResolution480) {
return types.PlexResolution480
}
if slices.Contains(resolutions, types.PlexResolution576) {
return types.PlexResolution576
}
if slices.Contains(resolutions, types.PlexResolution720) {
return types.PlexResolution720
}
if slices.Contains(resolutions, types.PlexResolution1080) {
return types.PlexResolution1080
}
if slices.Contains(resolutions, types.PlexResolution4K) {
return types.PlexResolution4K
}
return ""
}

func GetPlexTVEpisodes(ipAddress, plexToken, ratingKey string) (episodeList []types.PlexTVEpisode) {
url := fmt.Sprintf("http://%s:32400/library/metadata/%s/children?", ipAddress, ratingKey)

req, err := http.NewRequestWithContext(context.Background(), "GET", url, http.NoBody)
Expand All @@ -749,16 +784,6 @@ func GetPlexTVEpisodes(ipAddress, plexToken, ratingKey string, resolutions []str
}

episodeList = extractTVEpisodes(string(body))
if len(resolutions) > 0 {
// filter out episodes that don't match the resolution
var filteredEpisodes []types.PlexTVEpisode
for i := range episodeList {
if slices.Contains(resolutions, episodeList[i].Resolution) {
filteredEpisodes = append(filteredEpisodes, episodeList[i])
}
}
episodeList = filteredEpisodes
}
return episodeList
}

Expand Down Expand Up @@ -881,8 +906,16 @@ func extractTVEpisodes(xmlString string) (episodeList []types.PlexTVEpisode) {
}

for i := range container.Video {
intTime, err := strconv.ParseInt(container.Video[i].AddedAt, 10, 64)
var parsedDate time.Time
if err != nil {
parsedDate = time.Time{}
} else {
parsedDate = time.Unix(intTime, 0)
}
episodeList = append(episodeList, types.PlexTVEpisode{
Title: container.Video[i].Title, Resolution: container.Video[i].Media.VideoResolution, Index: container.Video[i].Index})
Title: container.Video[i].Title, Resolution: container.Video[i].Media.VideoResolution,
Index: container.Video[i].Index, DateAdded: parsedDate})
}
return episodeList
}
48 changes: 47 additions & 1 deletion plex/plex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func TestGetPlexTV(t *testing.T) {
if plexIP == "" || plexTVLibraryID == "" || plexToken == "" {
t.Skip("ACCEPTANCE TEST: PLEX environment variables not set")
}
result := GetPlexTV(plexIP, plexTVLibraryID, plexToken, []string{})
result := GetPlexTV(plexIP, plexTVLibraryID, plexToken)

if len(result) == 0 {
t.Errorf("Expected at least one TV show, but got %d", len(result))
Expand All @@ -78,6 +78,21 @@ func TestGetPlexTV(t *testing.T) {
}
}

func TestDebugGetPlexTVSeasons(t *testing.T) {
if plexIP == "" || plexTVLibraryID == "" || plexToken == "" {
t.Skip("ACCEPTANCE TEST: PLEX environment variables not set")
}
result := GetPlexTVSeasons(plexIP, plexToken, "5383")

if len(result) == 0 {
t.Errorf("Expected at least one TV show, but got %d", len(result))
}

if len(result[0].Episodes) == 0 {
t.Errorf("Expected at least one episode, but got %d", len(result[0].Episodes))
}
}

func TestGetPlexMusic(t *testing.T) {
if plexIP == "" || plexMusicLibraryID == "" || plexToken == "" {
t.Skip("ACCEPTANCE TEST: PLEX environment variables not set")
Expand All @@ -96,3 +111,34 @@ func TestGetPlexMusic(t *testing.T) {
t.Errorf("Expected at least one album, but got %d", len(result[0].Albums))
}
}

func Test_findLowestResolution(t *testing.T) {
tests := []struct {
name string
resolutions []string
wantLowestResolution string
}{
{
name: "SD is lowest",
resolutions: []string{types.PlexResolutionSD, types.PlexResolution240, types.PlexResolution720, types.PlexResolution1080},
wantLowestResolution: types.PlexResolutionSD,
},
{
name: "4k is lowest",
resolutions: []string{types.PlexResolution4K, types.PlexResolution4K},
wantLowestResolution: types.PlexResolution4K,
},
{
name: "720 is lowest",
resolutions: []string{types.PlexResolution720, types.PlexResolution1080, types.PlexResolution720, types.PlexResolution1080},
wantLowestResolution: types.PlexResolution720,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotLowestResolution := findLowestResolution(tt.resolutions); gotLowestResolution != tt.wantLowestResolution {
t.Errorf("findLowestResolution() = %v, want %v", gotLowestResolution, tt.wantLowestResolution)
}
})
}
}
Loading

0 comments on commit 484b027

Please sign in to comment.