Skip to content

Commit

Permalink
Add first draft
Browse files Browse the repository at this point in the history
  • Loading branch information
jyggen committed Jul 19, 2020
1 parent 7750809 commit 543b4f7
Show file tree
Hide file tree
Showing 5 changed files with 419 additions and 0 deletions.
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module github.com/jyggen/intro-detection-info

go 1.14

require (
github.com/briandowns/spinner v1.11.1
github.com/gammazero/workerpool v0.0.0-20200718004847-a282edc7fc33
github.com/gorilla/websocket v1.4.2 // indirect
github.com/jyggen/go-plex-client v0.0.0-20200718042431-44b8c7cbd22b
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/olekukonko/tablewriter v0.0.5-0.20200416053754-163badb3bac6
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 // indirect
)
44 changes: 44 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/briandowns/spinner v1.11.1 h1:OixPqDEcX3juo5AjQZAnFPbeUA0jvkp2qzB5gOZJ/L0=
github.com/briandowns/spinner v1.11.1/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
github.com/dgraph-io/badger v0.9.0/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/gammazero/deque v0.0.0-20200227231300-1e9af0e52b46 h1:iX4+rD9Fjdx8SkmSO/O5WAIX/j79ll3kuqv5VdYt9J8=
github.com/gammazero/deque v0.0.0-20200227231300-1e9af0e52b46/go.mod h1:D90+MBHVc9Sk1lJAbEVgws0eYEurY4mv2TDso3Nxh3w=
github.com/gammazero/workerpool v0.0.0-20200718004847-a282edc7fc33 h1:i5V8LnD1awKXQHIGWc9ovvk9QZJMIwjMEgo+uePAxd0=
github.com/gammazero/workerpool v0.0.0-20200718004847-a282edc7fc33/go.mod h1:/XWO2YAUUpPi3smDlFBl0vpX0JHwUomDM/oRMwRmnSs=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jyggen/go-plex-client v0.0.0-20200718042431-44b8c7cbd22b h1:WPNwfWwmVBGTMcDYpoMLx1H1ZJ5i9IuM4QZXJBlrWeU=
github.com/jyggen/go-plex-client v0.0.0-20200718042431-44b8c7cbd22b/go.mod h1:lVo4aXml4dq221aDN1tREseFrnx3hpEwPek97Qt6kFk=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/olekukonko/tablewriter v0.0.5-0.20200416053754-163badb3bac6 h1:F721VBMijn0OBFZ5wUSuMVVLQj2IJiiupn6UNd7UbBE=
github.com/olekukonko/tablewriter v0.0.5-0.20200416053754-163badb3bac6/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
171 changes: 171 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package main

import (
"fmt"
"github.com/briandowns/spinner"
"github.com/gammazero/workerpool"
"github.com/olekukonko/tablewriter"
"os"
"sort"
"strconv"
"sync"
"time"
)

type Result struct {
Show *Show
Season *Season
TotalEpisodesCount int
DetectedEpisodesCount int
MissingEpisodesList []*Episode
}

func main() {
name := os.Args[0]
args := os.Args[1:]

if len(args) != 2 {
errorAndExit(name, fmt.Errorf("exactly 2 command-line arguments expected, %d received", len(args)))
}

plex, err := NewPlex(args[0], args[1])

if err != nil {
errorAndExit(name, err)
}

wp := workerpool.New(10)
shows, err := plex.Shows()

if err != nil {
errorAndExit(name, err)
}

s := spinner.New(
spinner.CharSets[14],
250*time.Millisecond,
spinner.WithWriter(os.Stderr),
spinner.WithSuffix(" Fetching metadata..."),
spinner.WithFinalMSG("Metadata fetched.\n"),
)

s.Start()

errorsChan := make(chan error)
resultsChan := make(chan *Result)
errors := make([]error, 0)
results := make([]*Result, 0)

var channels sync.WaitGroup

channels.Add(2)

go func() {
defer channels.Done()

for err := range errorsChan {
errors = append(errors, err)
}
}()

go func() {
defer channels.Done()

for result := range resultsChan {
results = append(results, result)
}
}()

for _, show := range shows {
show := show
wp.Submit(func() {
seasons, err := show.Seasons()

if err != nil {
errorsChan <- err
return
}

for _, season := range seasons {
episodes, err := season.Episodes()

if err != nil {
errorsChan <- err
continue
}

result := &Result{
Show: show,
Season: season,
MissingEpisodesList: make([]*Episode, 0),
}

for _, episode := range episodes {
result.TotalEpisodesCount++

if episode.HasIntroMarker() {
result.DetectedEpisodesCount++
} else {
result.MissingEpisodesList = append(result.MissingEpisodesList, episode)
}
}

resultsChan <- result
}
})
}

wp.StopWait()

close(errorsChan)
close(resultsChan)

channels.Wait()

for _, err := range errors {
errorAndExit(name, err)
}

sort.Slice(results, func(i, j int) bool {
if results[i].Show.SortTitle() == results[j].Show.SortTitle() {
if results[i].Show.RatingKey() == results[j].Show.RatingKey() {
return results[i].Season.Number() < results[j].Season.Number()
}

return results[i].Show.RatingKey() < results[j].Show.RatingKey()
}

return results[i].Show.SortTitle() < results[j].Show.SortTitle()
})

table := tablewriter.NewWriter(os.Stdout)
table.SetAutoMergeCellsByColumnIndex([]int{0})
table.SetHeader([]string{"Show", "Season", "Detected", "Comment"})
table.SetRowLine(true)

for _, result := range results {
var status string
var color int
var missing string

if result.DetectedEpisodesCount == 0 {
status = "No"
color = tablewriter.FgRedColor
} else if result.DetectedEpisodesCount == result.TotalEpisodesCount {
status = "Yes"
color = tablewriter.FgGreenColor
} else {
status = "Partial"
color = tablewriter.FgYellowColor
missing = missingEpisodeString(result.MissingEpisodesList)
}

table.Rich(
[]string{result.Show.Title(), strconv.Itoa(result.Season.Number()), status, missing},
[]tablewriter.Colors{{}, {}, {tablewriter.Normal, color}},
)
}

s.Stop()
table.Render()
}
153 changes: 153 additions & 0 deletions plex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package main

import (
"fmt"
"github.com/jyggen/go-plex-client"
"strings"
"time"
)

type Plex struct {
connection *plex.Plex
}

func NewPlex(baseUrl string, token string) (*Plex, error) {
connection, err := plex.New(strings.TrimRight(baseUrl, "/"), token)

if err != nil {
return &Plex{}, err
}

connection.HTTPClient.Timeout = time.Second * 10

_, err = connection.Test()

if err != nil {
return &Plex{}, err
}

return &Plex{
connection: connection,
}, nil
}

func (p *Plex) Shows() ([]*Show, error) {
shows := make([]*Show, 0)
libraries, err := p.connection.GetLibraries()

if err != nil {
return shows, fmt.Errorf("unable to retrieve libraries: %w", err)
}

for _, library := range libraries.MediaContainer.Directory {
if library.Type != "show" {
continue
}

content, err := p.connection.GetLibraryContent(library.Key, "")

if err != nil {
return shows, fmt.Errorf("unable to retrieve content for library %v: %w", library.Key, err)
}

for _, show := range content.MediaContainer.Metadata {
shows = append(shows, &Show{
connection: p.connection,
metadata: show,
})
}
}

return shows, nil
}

type Show struct {
connection *plex.Plex
metadata plex.Metadata
}

func (s *Show) Seasons() ([]*Season, error) {
seasons := make([]*Season, 0)
children, err := s.connection.GetMetadataChildren(s.metadata.RatingKey)

if err != nil {
return seasons, err
}

for _, season := range children.MediaContainer.Metadata {
seasons = append(seasons, &Season{
connection: s.connection,
metadata: season,
})
}

return seasons, nil
}

func (s *Show) RatingKey() string {
return s.metadata.RatingKey
}

func (s *Show) SortTitle() string {
if s.metadata.TitleSort == "" {
return s.metadata.Title
}

return s.metadata.TitleSort
}

func (s *Show) Title() string {
return s.metadata.Title
}

type Season struct {
connection *plex.Plex
metadata plex.Metadata
}

func (s *Season) Episodes() ([]*Episode, error) {
episodes := make([]*Episode, 0)
children, err := s.connection.GetMetadataChildren(s.metadata.RatingKey)

if err != nil {
return episodes, err
}

for _, episode := range children.MediaContainer.Metadata {
metadata, err := s.connection.GetMetadata(episode.RatingKey)

if err != nil {
return episodes, err
}

episodes = append(episodes, &Episode{
connection: s.connection,
metadata: metadata.MediaContainer.Metadata[0],
})
}

return episodes, nil
}

func (e *Season) Number() int {
return int(e.metadata.Index)
}

type Episode struct {
connection *plex.Plex
metadata plex.Metadata
}

func (e *Episode) HasIntroMarker() bool {
for _, marker := range e.metadata.Marker {
if marker.Type == "intro" {
return true
}
}

return false
}

func (e *Episode) Number() int {
return int(e.metadata.Index)
}
Loading

0 comments on commit 543b4f7

Please sign in to comment.