package handler import ( "fmt" "io" "net/http" "os" "strings" "sync" "time" "github.com/dustin/go-humanize" "github.com/gorilla/mux" "github.com/sirupsen/logrus" "gopkg.in/h2non/filetype.v1" "git.adphi.net/adphi/ytsflix/engine" "git.adphi.net/adphi/ytsflix/templates" "git.adphi.net/adphi/ytsflix/ytsclient" ) var ( _ io.Reader = (*os.File)(nil) ) type Handler struct { engine *engine.Engine clients map[string]int yts ytsclient.Client mutex sync.Mutex storagePath string } type HomeData struct { Categories map[string]map[string]Movie } type Movie struct { Title string Year int Cover string Link string Synopsis string Genre []string Youtube string Casting []Cast } type Cast struct { Name string Image string } type MovieData struct { Title string Link string Subtitles map[string]string } func NewHandler(storagePath string) (*Handler, error) { h := Handler{storagePath: storagePath} var err error h.engine, err = engine.NewEngine(storagePath) if err != nil { return nil, err } h.yts = ytsclient.NewClient() h.clients = map[string]int{} return &h, nil } func (h *Handler) Close() { h.engine.Close() } func (h *Handler) Home(writer http.ResponseWriter, request *http.Request) { hd := HomeData{Categories: map[string]map[string]Movie{}} var mu sync.Mutex var wg sync.WaitGroup wg.Add(len(ytsclient.Genres)) for _, g := range ytsclient.Genres { go func(genre ytsclient.Genre) { defer wg.Done() res, err := h.yts.List(&ytsclient.ListParams{ Genre: genre, Limit: 50, SortBy: ytsclient.Year, OrderBy: ytsclient.Desc, Quality: ytsclient.Quality1080p, }) if err != nil { logrus.WithField("genre", genre).Error(err) return } ms := map[string]Movie{} for _, m := range res { ms[m.Title] = Movie{ Link: fmt.Sprintf("/movie/%d", m.ID), Cover: m.MediumCoverImage, } } if len(ms) >= 10 { mu.Lock() hd.Categories[string(genre)] = ms mu.Unlock() } }(g) } wg.Wait() t := templates.HomeTemplate() writer.Header().Set("Content-Type", "text/html; charset=utf-8") writer.WriteHeader(200) t.Execute(writer, hd) //fmt.Fprint(writer, html) } func (h *Handler) Movie(w http.ResponseWriter, r *http.Request) { id := mux.Vars(r)["id"] m, err := h.yts.Get(id, nil) if err != nil { sendError(w, err) } go func() { h.engine.Prepare(getMagnet(m)) }() t := templates.MovieTemplate() var cast []Cast for _, c := range m.Cast { cast = append(cast, Cast{Name: c.Name, Image: c.URLSmallImage}) } md := Movie{ Title: m.Title, Year: m.Year, Genre: m.Genres, Cover: m.MediumCoverImage, Link: fmt.Sprintf("/torrent/%d", m.ID), Synopsis: m.DescriptionFull, Youtube: fmt.Sprintf("https://www.youtube.com/embed/%s", m.YtTrailerCode), Casting: cast, } w.Header().Set("Content-Type", "text/html; charset=utf-8") t.Execute(w, md) } func (h *Handler) Category(w http.ResponseWriter, r *http.Request) { category := mux.Vars(r)["category"] res, err := h.yts.List(&ytsclient.ListParams{Genre: ytsclient.Genre(category), Limit: 50, Page: 1}) if err != nil { sendError(w, err) } mm := map[string]Movie{} for _, m := range res { mm[m.Title] = Movie{ Link: fmt.Sprintf("/movie/%d", m.ID), Cover: m.MediumCoverImage, } } d := struct { Category string Movies map[string]Movie }{ Category: category, Movies: mm, } t := templates.ListTemplate() w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(200) t.Execute(w, d) } func (h *Handler) Search(writer http.ResponseWriter, request *http.Request) { vars := mux.Vars(request) s := vars["search"] res, err := h.yts.Search(s, &ytsclient.ListParams{Quality: ytsclient.Quality1080p}) if err != nil { sendError(writer, err) return } mm := map[string]Movie{} for _, m := range res { mm[m.Title] = Movie{ Link: fmt.Sprintf("/movie/%d", m.ID), Cover: m.MediumCoverImage, } } d := struct { Category string Movies map[string]Movie }{ Category: fmt.Sprintf("Search: %s", s), Movies: mm, } t := templates.ListTemplate() writer.Header().Set("Content-Type", "text/html; charset=utf-8") writer.WriteHeader(200) t.Execute(writer, d) } func (h *Handler) Torrents(writer http.ResponseWriter, request *http.Request) { id := mux.Vars(request)["id"] m, err := h.yts.Get(id, nil) if err != nil { sendError(writer, err) return } magnet := getMagnet(m) mt, err := h.engine.Download(magnet) if err != nil { sendError(writer, err) return } if err := mt.DownloadSubtitles(m.ImdbCode); err != nil { logrus.Error(err) } cp := mt.Progress() go func() { for p := range cp { hSize := humanize.Bytes(uint64(p.Size)) downloadSpeed := humanize.Bytes(p.DownloadSpeed) + "/s" complete := humanize.Bytes(p.Downloaded) logrus.Debugf("%s: Progress: %s / %s %s", mt.FileName, complete, hSize, downloadSpeed) } }() link := fmt.Sprintf("/watch/%s", mt.FileName) ss := map[string]string{} for s, v := range mt.Subtitles { ss[strings.Replace(s, ".vtt", "", -1)] = fmt.Sprintf("/subs/%s", v) } md := MovieData{Title: mt.FileName, Link: link, Subtitles: ss} writer.Header().Set("Content-Type", "text/html; charset=utf-8") writer.WriteHeader(200) t := templates.WatchTemplate() t.Execute(writer, md) } func (h *Handler) Serve(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) var mt *engine.MovieTorrent var err error movie := vars["movie"] h.mutex.Lock() h.clients[movie]++ h.mutex.Unlock() if mt, err = h.engine.Get(movie); err != nil { http.Error(w, "file not found", http.StatusBadRequest) return } reader := mt.NewReader() defer reader.Close() defer func() { h.mutex.Lock() h.clients[movie]-- h.mutex.Unlock() time.Sleep(5 * time.Second) if h.clients[movie] == 0 { logrus.Infof("The client closed the connection. Cleaning up: %s.", movie) mt.Cancel() } }() w.Header().Set("Connection", "Keep-Alive") w.Header().Set("Content-Type", mt.MIME) http.ServeContent(w, r, mt.FileName, time.Now(), reader) } func (h *Handler) Sub(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) m := vars["movie"] s := vars["sub"] logrus.Debugf("requesting subs: movie: %s, sub: %s", vars["movie"], vars["sub"]) f, err := os.Open(fmt.Sprintf("%s/%s/%s", h.storagePath, m, s)) if err != nil { sendError(w, err) return } mime := filetype.GetType(".vtt").MIME.Value w.Header().Add("Context-Type", mime) http.ServeContent(w, r, vars["sub"], time.Now(), f) } func sendError(writer http.ResponseWriter, err error) { logrus.Error(err) http.Error(writer, err.Error(), http.StatusInternalServerError) } func getMagnet(m ytsclient.Movie) string { var to *ytsclient.Torrent for _, t := range m.Torrents { if t.Quality == string(ytsclient.Quality1080p) { to = &t break } } if to == nil { to = &m.Torrents[0] } magnet, _ := m.Magnet(*to) return magnet }