diff --git a/TODO b/TODO
index fed6447..3636cde 100644
--- a/TODO
+++ b/TODO
@@ -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
@@ -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 ?
diff --git a/amazon/amazon.go b/amazon/amazon.go
index 7e0acd3..153bb42 100644
--- a/amazon/amazon.go
+++ b/amazon/amazon.go
@@ -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 {
@@ -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)
}()
}
@@ -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 {
@@ -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(`(.*?)`)
match := r.FindStringSubmatch(response)
@@ -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, ``)
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 {
@@ -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 {
diff --git a/cmd/root.go b/cmd/root.go
index 0faf915..e42302e 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -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
diff --git a/musicbrainz/musicbrainz_test.go b/musicbrainz/musicbrainz_test.go
index 990d38e..71a18fa 100644
--- a/musicbrainz/musicbrainz_test.go
+++ b/musicbrainz/musicbrainz_test.go
@@ -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),
},
},
},
diff --git a/plex/plex.go b/plex/plex.go
index b61021a..dbff20b 100644
--- a/plex/plex.go
+++ b/plex/plex.go
@@ -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)
@@ -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
@@ -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)
@@ -707,7 +707,7 @@ 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
}
@@ -715,14 +715,49 @@ func GetPlexTVSeasons(ipAddress, plexToken, ratingKey string, resolutions []stri
// 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)
@@ -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
}
@@ -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
}
diff --git a/plex/plex_test.go b/plex/plex_test.go
index 4b7d271..c4eff6b 100644
--- a/plex/plex_test.go
+++ b/plex/plex_test.go
@@ -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))
@@ -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")
@@ -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)
+ }
+ })
+ }
+}
diff --git a/types/types.go b/types/types.go
index 4a3e706..6e59e53 100644
--- a/types/types.go
+++ b/types/types.go
@@ -3,11 +3,18 @@ package types
import "time"
const (
- DiskBluray = "Blu-ray"
- DiskDVD = "DVD"
- Disk4K = "4K Blu-ray"
- PlexMovieType = "Movie"
- ConcurrencyLimit = 10
+ DiskBluray = "Blu-ray"
+ DiskDVD = "DVD"
+ Disk4K = "4K Blu-ray"
+ PlexMovieType = "Movie"
+ PlexResolutionSD = "sd"
+ PlexResolution240 = "240"
+ PlexResolution480 = "480"
+ PlexResolution576 = "576"
+ PlexResolution720 = "720"
+ PlexResolution1080 = "1080"
+ PlexResolution4K = "4k"
+ ConcurrencyLimit = 10
)
type SearchResults struct {
@@ -60,16 +67,19 @@ type PlexTVShow struct {
}
type PlexTVSeason struct {
- Title string
- Number int
- RatingKey string
- Episodes []PlexTVEpisode
+ Title string
+ Number int
+ RatingKey string
+ LowestResolution string
+ LastEpisodeAdded time.Time
+ Episodes []PlexTVEpisode
}
type PlexTVEpisode struct {
Title string
Index string
Resolution string
+ DateAdded time.Time
}
type TVSearchResult struct {
@@ -82,10 +92,10 @@ type TVSearchResult struct {
ReleaseDate time.Time
NewRelease bool
BoxSet bool
- Series []TVSeriesResult
+ Seasons []TVSeasonResult
}
-type TVSeriesResult struct {
+type TVSeasonResult struct {
Number int
URL string
Format []string
diff --git a/web/movies.go b/web/movies.go
index 46c8082..69876b4 100644
--- a/web/movies.go
+++ b/web/movies.go
@@ -39,13 +39,13 @@ func moviesHandler(w http.ResponseWriter, _ *http.Request) {
func processMoviesHTML(w http.ResponseWriter, r *http.Request) {
lookup := r.FormValue("lookup")
// plex resolutions
- sd := r.FormValue("sd")
+ sd := r.FormValue("SD")
r240 := r.FormValue("240p")
r480 := r.FormValue("480p")
r576 := r.FormValue("576p")
r720 := r.FormValue("720p")
r1080 := r.FormValue("1080p")
- r4k := r.FormValue("4k")
+ r4k := r.FormValue("4K")
plexResolutions := []string{sd, r240, r480, r576, r720, r1080, r4k}
// remove empty resolutions
var filteredResolutions []string
@@ -82,7 +82,7 @@ func processMoviesHTML(w http.ResponseWriter, r *http.Request) {
}
// if we are filtering by newer version, we need to search again
if newerVersion == stringTrue {
- scrapedResults := amazon.ScrapeMovies(&searchResult)
+ scrapedResults := amazon.ScrapeTitles(&searchResult)
searchResult.MovieSearchResults = scrapedResults
}
}
diff --git a/web/movies.html b/web/movies.html
index b52b0a0..f6e88c2 100644
--- a/web/movies.html
+++ b/web/movies.html
@@ -30,28 +30,28 @@ Movies