func Authorize(fromSettings bool) error { code, err := GetCode() if err != nil { xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) return err } log.Noticef("Got code for %s: %s", code.VerificationURL, code.UserCode) if xbmc.Dialog("LOCALIZE[30058]", fmt.Sprintf("Visit %s and enter your code: %s", code.VerificationURL, code.UserCode)) == false { return errors.New("Authentication canceled.") } token, err := PollToken(code) if err != nil { xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) return err } success := "Woohoo!" if fromSettings { success += " (Save your settings!)" } xbmc.Notify("Quasar", success, config.AddonIcon()) xbmc.SetSetting("trakt_token", token.AccessToken) xbmc.SetSetting("trakt_refresh_token", token.RefreshToken) return nil }
func GetList(listId string, language string, page int) Movies { var results *List listResultsPerPage := config.Get().ResultsPerPage rateLimiter.Call(func() { urlValues := napping.Params{ "api_key": apiKey, }.AsUrlValues() resp, err := napping.Get( tmdbEndpoint+"list/"+listId, &urlValues, &results, nil, ) if err != nil { log.Error(err.Error()) xbmc.Notify("Quasar", "GetList failed, check your logs.", config.AddonIcon()) } else if resp.Status() != 200 { message := fmt.Sprintf("GetList bad status: %d", resp.Status()) log.Error(message) xbmc.Notify("Quasar", message, config.AddonIcon()) } }) tmdbIds := make([]int, 0, listResultsPerPage) for i, movie := range results.Items { if i < page*listResultsPerPage { continue } tmdbIds = append(tmdbIds, movie.Id) if i >= (startPage+page)*listResultsPerPage-1 { break } } return GetMovies(tmdbIds, language) }
func ListShows(endpoint string, params napping.Params, page int) (shows Shows) { var results *EntityList params["page"] = strconv.Itoa(startPage + page) params["api_key"] = apiKey p := params.AsUrlValues() rateLimiter.Call(func() { resp, err := napping.Get( tmdbEndpoint + endpoint, &p, &results, nil, ) if err != nil { log.Error(err.Error()) xbmc.Notify("Quasar", "ListShows failed, check your logs.", config.AddonIcon()) } else if resp.Status() != 200 { message := fmt.Sprintf("ListShows bad status: %d", resp.Status()) log.Error(message) xbmc.Notify("Quasar", message, config.AddonIcon()) } }) if results != nil { for _, show := range results.Results { shows = append(shows, GetShow(show.Id, params["language"])) } } return shows }
func SearchShows(query string, language string, page int) Shows { var results EntityList rateLimiter.Call(func() { urlValues := napping.Params{ "api_key": apiKey, "query": query, "page": strconv.Itoa(startPage + page), }.AsUrlValues() resp, err := napping.Get( tmdbEndpoint+"search/tv", &urlValues, &results, nil, ) if err != nil { log.Error(err.Error()) xbmc.Notify("Quasar", "SearchShows failed, check your logs.", config.AddonIcon()) } else if resp.Status() != 200 { message := fmt.Sprintf("SearchShows bad status: %d", resp.Status()) log.Error(message) xbmc.Notify("Quasar", message, config.AddonIcon()) } }) tmdbIds := make([]int, 0, len(results.Results)) for _, entity := range results.Results { tmdbIds = append(tmdbIds, entity.Id) } return GetShows(tmdbIds, language) }
func Find(externalId string, externalSource string) *FindResult { var result *FindResult cacheStore := cache.NewFileStore(path.Join(config.Get().ProfilePath, "cache")) key := fmt.Sprintf("com.tmdb.find.%s.%s", externalSource, externalId) if err := cacheStore.Get(key, &result); err != nil { rateLimiter.Call(func() { urlValues := napping.Params{ "api_key": apiKey, "external_source": externalSource, }.AsUrlValues() resp, err := napping.Get( tmdbEndpoint+"find/"+externalId, &urlValues, &result, nil, ) if err != nil { log.Error(err.Error()) xbmc.Notify("Quasar", "Failed Find call, check your logs.", config.AddonIcon()) } else if resp.Status() != 200 { message := fmt.Sprintf("Find call bad status: %d", resp.Status()) log.Error(message) xbmc.Notify("Quasar", message, config.AddonIcon()) } cacheStore.Set(key, result, 365*24*time.Hour) }) } return result }
func ListShowsComplete(endpoint string, params napping.Params, page int) Shows { maxPages := MaxPages if page >= 0 { maxPages = 1 } shows := make(Shows, maxPages*resultsPerPage) params["api_key"] = apiKey wg := sync.WaitGroup{} for i := 0; i < maxPages; i++ { wg.Add(1) currentpage := i startIndex := i * resultsPerPage if page >= 0 { currentpage = page } go func(page int) { defer wg.Done() var tmp *EntityList tmpParams := napping.Params{ "page": strconv.Itoa(startPage + page), } for k, v := range params { tmpParams[k] = v } urlValues := tmpParams.AsUrlValues() rateLimiter.Call(func() { resp, err := napping.Get( tmdbEndpoint+endpoint, &urlValues, &tmp, nil, ) if err != nil { log.Error(err.Error()) xbmc.Notify("Quasar", "ListShows failed, check your logs.", config.AddonIcon()) } else if resp.Status() != 200 { message := fmt.Sprintf("ListShows bad status: %d", resp.Status()) xbmc.Notify("Quasar", message, config.AddonIcon()) } }) if tmp != nil { for i, entity := range tmp.Results { shows[startIndex+i] = GetShow(entity.Id, params["language"]) } } }(currentpage) } wg.Wait() return shows }
func RemoveMovieFromWatchlist(ctx *gin.Context) { tmdbId := ctx.Params.ByName("tmdbId") resp, err := trakt.RemoveFromWatchlist("movies", tmdbId) if err != nil { xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) } else if resp.Status() != 200 { xbmc.Notify("Quasar", fmt.Sprintf("Failed with %d status code", resp.Status()), config.AddonIcon()) } else { xbmc.Notify("Quasar", "Movie removed from watchlist", config.AddonIcon()) os.RemoveAll(filepath.Join(config.Get().Info.Profile, "cache")) xbmc.Refresh() } }
func AddMovieToCollection(ctx *gin.Context) { tmdbId := ctx.Params.ByName("tmdbId") resp, err := trakt.AddToCollection("movies", tmdbId) if err != nil { xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) } else if resp.Status() != 201 { xbmc.Notify("Quasar", fmt.Sprintf("Failed with %d status code", resp.Status()), config.AddonIcon()) } else { xbmc.Notify("Quasar", "Movie added to collection", config.AddonIcon()) os.RemoveAll(filepath.Join(config.Get().Info.Profile, "cache")) xbmc.Refresh() } }
func AddShowToWatchlist(ctx *gin.Context) { tmdbId := ctx.Params.ByName("showId") resp, err := trakt.AddToWatchlist("shows", tmdbId) if err != nil { xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) } else if resp.Status() != 201 { xbmc.Notify("Quasar", fmt.Sprintf("Failed %d", resp.Status()), config.AddonIcon()) } else { xbmc.Notify("Quasar", "Show added to watchlist", config.AddonIcon()) os.RemoveAll(filepath.Join(config.Get().Info.Profile, "cache")) xbmc.Refresh() } }
func GetShow(showId int, language string) *Show { var show *Show cacheStore := cache.NewFileStore(path.Join(config.Get().ProfilePath, "cache")) key := fmt.Sprintf("com.tmdb.show.%d.%s", showId, language) if err := cacheStore.Get(key, &show); err != nil { rateLimiter.Call(func() { urlValues := napping.Params{ "api_key": apiKey, "append_to_response": "credits,images,alternative_titles,translations,external_ids", "language": language, }.AsUrlValues() resp, err := napping.Get( tmdbEndpoint+"tv/"+strconv.Itoa(showId), &urlValues, &show, nil, ) if err != nil { switch e := err.(type) { case *json.UnmarshalTypeError: log.Errorf("UnmarshalTypeError: Value[%s] Type[%v] Offset[%d] for %d", e.Value, e.Type, e.Offset, showId) case *json.InvalidUnmarshalError: log.Errorf("InvalidUnmarshalError: Type[%v]", e.Type) default: log.Error(err.Error()) } LogError(err) xbmc.Notify("Quasar", "GetShow failed, check your logs.", config.AddonIcon()) } else if resp.Status() != 200 { message := fmt.Sprintf("GetShow bad status: %d", resp.Status()) log.Error(message) xbmc.Notify("Quasar", message, config.AddonIcon()) } }) if show != nil { cacheStore.Set(key, show, cacheTime) } } if show == nil { return nil } switch t := show.RawPopularity.(type) { case string: if popularity, err := strconv.ParseFloat(t, 64); err == nil { show.Popularity = popularity } case float64: show.Popularity = t } return show }
func GetSeason(showId int, seasonNumber int, language string) *Season { var season *Season cacheStore := cache.NewFileStore(path.Join(config.Get().ProfilePath, "cache")) key := fmt.Sprintf("com.tmdb.season.%d.%d.%s", showId, seasonNumber, language) if err := cacheStore.Get(key, &season); err != nil { rateLimiter.Call(func() { urlValues := napping.Params{ "api_key": apiKey, "append_to_response": "credits,images,videos,external_ids", "language": language, }.AsUrlValues() resp, err := napping.Get( fmt.Sprintf("%stv/%d/season/%d", tmdbEndpoint, showId, seasonNumber), &urlValues, &season, nil, ) if err != nil { log.Error(err.Error()) xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) } else if resp.Status() != 200 { message := fmt.Sprintf("GetSeason bad status: %d", resp.Status()) log.Error(message) xbmc.Notify("Quasar", message, config.AddonIcon()) } }) season.EpisodeCount = len(season.Episodes) // Fix for shows that have translations but return empty strings // for episode names and overviews. // We detect if episodes have their name filled, and if not re-query // with no language set. // See https://github.com/scakemyer/plugin.video.quasar/issues/249 if season.EpisodeCount > 0 { for index := 0; index < season.EpisodeCount; index++ { if season.Episodes[index].Name == "" { season.Episodes[index] = GetEpisode(showId, seasonNumber, index+1, "") } } } if season != nil { cacheStore.Set(key, season, cacheTime) } } if season == nil { return nil } return season }
func WatchlistMovies(ctx *gin.Context) { movies, err := trakt.WatchlistMovies() if err != nil { xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) } renderTraktMovies(movies, ctx, 0) }
func ShowEpisodePlay(ctx *gin.Context) { tmdbId := ctx.Params.ByName("showId") showId, _ := strconv.Atoi(tmdbId) seasonNumber, _ := strconv.Atoi(ctx.Params.ByName("season")) episodeNumber, _ := strconv.Atoi(ctx.Params.ByName("episode")) show := tmdb.GetShow(showId, "") episode := tmdb.GetEpisode(showId, seasonNumber, episodeNumber, "") runtime := 45 if len(show.EpisodeRunTime) > 0 { runtime = show.EpisodeRunTime[len(show.EpisodeRunTime)-1] } torrents, err := showEpisodeLinks(showId, seasonNumber, episodeNumber) if err != nil { ctx.Error(err) return } if len(torrents) == 0 { xbmc.Notify("Quasar", "LOCALIZE[30205]", config.AddonIcon()) return } AddToTorrentsMap(strconv.Itoa(episode.Id), torrents[0]) rUrl := UrlQuery(UrlForXBMC("/play"), "uri", torrents[0].Magnet(), "tmdb", strconv.Itoa(episode.Id), "type", "episode", "runtime", strconv.Itoa(runtime)) ctx.Redirect(302, rUrl) }
func RemoveShow(ctx *gin.Context) { LibraryPath := config.Get().LibraryPath ShowsLibraryPath := filepath.Join(LibraryPath, "Shows") DBPath := filepath.Join(LibraryPath, fmt.Sprintf("%s.json", DBName)) showId := ctx.Params.ByName("showId") show, err := tvdb.NewShow(showId, "en") if err != nil { ctx.String(404, "") return } ShowPath := filepath.Join(ShowsLibraryPath, toFileName(show.SeriesName)) if err := RemoveFromJsonDB(DBPath, showId, LShow); err != nil { libraryLog.Info("Unable to remove show from db") ctx.String(404, "") return } if err := os.RemoveAll(ShowPath); err != nil { libraryLog.Info("Unable to remove show folder") ctx.String(404, "") return } xbmc.Notify("Quasar", "LOCALIZE[30222]", config.AddonIcon()) ctx.String(200, "") xbmc.VideoLibraryClean() libraryLog.Info("Show removed") }
func RemoveMovie(ctx *gin.Context) { LibraryPath := config.Get().LibraryPath MoviesLibraryPath := filepath.Join(LibraryPath, "Movies") DBPath := filepath.Join(LibraryPath, fmt.Sprintf("%s.json", DBName)) imdbId := ctx.Params.ByName("imdbId") movie := tmdb.GetMovieFromIMDB(imdbId, "en") MovieStrm := toFileName(fmt.Sprintf("%s (%s)", movie.OriginalTitle, strings.Split(movie.ReleaseDate, "-")[0])) MoviePath := filepath.Join(MoviesLibraryPath, MovieStrm) if err := RemoveFromJsonDB(DBPath, imdbId, LMovie); err != nil { libraryLog.Info("Unable to remove movie from db") ctx.String(404, "") return } if err := os.RemoveAll(MoviePath); err != nil { libraryLog.Info("Unable to remove movie folder") ctx.String(404, "") return } xbmc.Notify("Quasar", "LOCALIZE[30222]", config.AddonIcon()) ctx.String(200, "") xbmc.VideoLibraryClean() libraryLog.Info("Movie removed") }
func TraktBoxOffice(ctx *gin.Context) { movies, err := trakt.TopMovies("boxoffice", "1") if err != nil { xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) } renderTraktMovies(movies, ctx, 0) }
func RemoveShow(ctx *gin.Context) { LibraryPath := config.Get().LibraryPath ShowsLibraryPath := filepath.Join(LibraryPath, "Shows") DBPath := filepath.Join(LibraryPath, fmt.Sprintf("%s.json", DBName)) showId := ctx.Params.ByName("showId") Id, _ := strconv.Atoi(showId) show := tmdb.GetShow(Id, "en") if show == nil { ctx.String(404, "") return } ShowStrm := toFileName(fmt.Sprintf("%s (%s)", show.Name, strings.Split(show.FirstAirDate, "-")[0])) ShowPath := filepath.Join(ShowsLibraryPath, ShowStrm) if err := RemoveFromJsonDB(DBPath, showId, LShow); err != nil { libraryLog.Error("Unable to remove show from db") ctx.String(404, "") return } if err := os.RemoveAll(ShowPath); err != nil { libraryLog.Error("Unable to remove show folder") ctx.String(404, "") return } xbmc.Notify("Quasar", "LOCALIZE[30222]", config.AddonIcon()) ctx.String(200, "") xbmc.VideoLibraryClean() ClearCache(ctx) xbmc.Refresh() libraryLog.Notice("Show removed") }
func CollectionShows(ctx *gin.Context) { shows, err := trakt.CollectionShows() if err != nil { xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) } renderTraktShows(shows, ctx, 0) }
func ProviderCheck(ctx *gin.Context) { addonId := ctx.Params.ByName("provider") failures := xbmc.AddonCheck(addonId) translated := xbmc.GetLocalizedString(30243) xbmc.Notify("Quasar", fmt.Sprintf("%s: %d", translated, failures), config.AddonIcon()) ctx.String(200, "") }
func ShowEpisodeLinks(ctx *gin.Context) { seasonNumber, _ := strconv.Atoi(ctx.Params.ByName("season")) episodeNumber, _ := strconv.Atoi(ctx.Params.ByName("episode")) torrents, err := showEpisodeLinks(ctx.Params.ByName("showId"), seasonNumber, episodeNumber) if err != nil { ctx.Error(err) return } if len(torrents) == 0 { xbmc.Notify("Quasar", "LOCALIZE[30205]", config.AddonIcon()) return } choices := make([]string, 0, len(torrents)) for _, torrent := range torrents { label := fmt.Sprintf("S:%d P:%d - %s", torrent.Seeds, torrent.Peers, torrent.Name, ) choices = append(choices, label) } choice := xbmc.ListDialog("LOCALIZE[30202]", choices...) if choice >= 0 { rUrl := UrlQuery(UrlForXBMC("/play"), "uri", torrents[choice].Magnet()) ctx.Redirect(302, rUrl) } }
func (btp *BTPlayer) addTorrent() error { btp.log.Info("Adding torrent") if btp.bts.config.DownloadPath == "." { xbmc.Notify("Quasar", "LOCALIZE[30113]", config.AddonIcon()) return fmt.Errorf("Download path empty") } if status, err := diskusage.DiskUsage(btp.bts.config.DownloadPath); err != nil { btp.bts.log.Warningf("Unable to retrieve the free space for %s, continuing anyway...", btp.bts.config.DownloadPath) } else { btp.diskStatus = status } torrentParams := libtorrent.NewAddTorrentParams() defer libtorrent.DeleteAddTorrentParams(torrentParams) torrentParams.SetUrl(btp.uri) btp.log.Infof("Setting save path to %s", btp.bts.config.DownloadPath) torrentParams.SetSavePath(btp.bts.config.DownloadPath) btp.log.Infof("Checking for fast resume data in %s.fastresume", btp.infoHash) fastResumeFile := filepath.Join(btp.bts.config.TorrentsPath, fmt.Sprintf("%s.fastresume", btp.infoHash)) if _, err := os.Stat(fastResumeFile); err == nil { btp.log.Info("Found fast resume data...") btp.fastResumeFile = fastResumeFile fastResumeData, err := ioutil.ReadFile(fastResumeFile) if err != nil { return err } fastResumeVector := libtorrent.NewStdVectorChar() for _, c := range fastResumeData { fastResumeVector.PushBack(c) } torrentParams.SetResumeData(fastResumeVector) } btp.torrentHandle = btp.bts.Session.AddTorrent(torrentParams) go btp.consumeAlerts() if btp.torrentHandle == nil { return fmt.Errorf("Unable to add torrent with URI %s", btp.uri) } btp.log.Info("Enabling sequential download") btp.torrentHandle.SetSequentialDownload(true) status := btp.torrentHandle.Status(uint(libtorrent.TorrentHandleQueryName)) btp.torrentName = status.GetName() btp.log.Infof("Downloading %s", btp.torrentName) if status.GetHasMetadata() == true { btp.onMetadataReceived() } return nil }
func main() { // Make sure we are properly multithreaded. runtime.GOMAXPROCS(runtime.NumCPU()) logging.SetFormatter(logging.MustStringFormatter("%{time:2006-01-02 15:04:05} %{level:.4s} %{module:-15s} %{message}")) logging.SetBackend(logging.NewLogBackend(os.Stdout, "", 0)) for _, line := range strings.Split(QuasarLogo, "\n") { log.Info(line) } log.Info("Version: %s Git: %s Go: %s", util.Version, util.GitCommit, runtime.Version()) conf := config.Reload() ensureSingleInstance() Migrate() xbmc.CloseAllDialogs() log.Info("Addon: %s v%s", conf.Info.Id, conf.Info.Version) btService := bittorrent.NewBTService(*makeBTConfiguration(conf)) var shutdown = func() { log.Info("Shutting down...") btService.Close() log.Info("Bye bye") os.Exit(0) } var watchParentProcess = func() { for { // did the parent die? shutdown! if os.Getppid() == 1 { log.Warning("Parent shut down. Me too.") go shutdown() break } time.Sleep(1 * time.Second) } } go watchParentProcess() http.Handle("/", api.Routes(btService)) http.Handle("/files/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handler := http.StripPrefix("/files/", http.FileServer(bittorrent.NewTorrentFS(btService, config.Get().DownloadPath))) handler.ServeHTTP(w, r) })) http.Handle("/reload", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { btService.Reconfigure(*makeBTConfiguration(config.Reload())) })) http.Handle("/shutdown", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { shutdown() })) xbmc.Notify("Quasar", "LOCALIZE[30208]", config.AddonIcon()) http.ListenAndServe(":"+strconv.Itoa(config.ListenPort), nil) }
func TraktMostCollectedMovies(ctx *gin.Context) { pageParam := ctx.DefaultQuery("page", "1") page, _ := strconv.Atoi(pageParam) movies, err := trakt.TopMovies("collected", pageParam) if err != nil { xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) } renderTraktMovies(movies, ctx, page) }
func TraktMostAnticipatedShows(ctx *gin.Context) { pageParam := ctx.DefaultQuery("page", "1") page, _ := strconv.Atoi(pageParam) shows, err := trakt.TopShows("anticipated", pageParam) if err != nil { xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) } renderTraktShows(shows, ctx, page) }
func AuthorizeTrakt(ctx *gin.Context) { err := trakt.Authorize(true) if err == nil { ctx.String(200, "") } else { xbmc.Notify("Quasar", err.Error(), config.AddonIcon()) ctx.String(200, "") } }
func AddShow(ctx *gin.Context) { LibraryPath := config.Get().LibraryPath DBPath := filepath.Join(LibraryPath, fmt.Sprintf("%s.json", DBName)) if fileInfo, err := os.Stat(LibraryPath); err != nil || fileInfo.IsDir() == false || LibraryPath == "" || LibraryPath == "." { xbmc.Notify("Quasar", "LOCALIZE[30220]", config.AddonIcon()) ctx.String(404, "") return } showId := ctx.Params.ByName("showId") if inJsonDb, err := InJsonDB(showId, LShow); err != nil || inJsonDb == true { ctx.String(404, "") return } ShowsLibraryPath := filepath.Join(LibraryPath, "Shows") if _, err := os.Stat(ShowsLibraryPath); os.IsNotExist(err) { if err := os.Mkdir(ShowsLibraryPath, 0755); err != nil { libraryLog.Error("Unable to create library path for Shows") ctx.String(404, "") return } } if err := WriteShowStrm(showId, ShowsLibraryPath); err != nil { ctx.String(404, "") return } if err := UpdateJsonDB(DBPath, showId, LShow); err != nil { libraryLog.Error("Unable to update json DB") ctx.String(404, "") return } xbmc.Notify("Quasar", "LOCALIZE[30221]", config.AddonIcon()) ctx.String(200, "") xbmc.VideoLibraryScan() ClearCache(ctx) xbmc.Refresh() libraryLog.Notice("Show added") }
func ShowEpisodeLinks(ctx *gin.Context) { seasonNumber, _ := strconv.Atoi(ctx.Params.ByName("season")) episodeNumber, _ := strconv.Atoi(ctx.Params.ByName("episode")) torrents, longName, err := showEpisodeLinks(ctx.Params.ByName("showId"), seasonNumber, episodeNumber) if err != nil { ctx.Error(err) return } if len(torrents) == 0 { xbmc.Notify("Quasar", "LOCALIZE[30205]", config.AddonIcon()) return } choices := make([]string, 0, len(torrents)) for _, torrent := range torrents { info := make([]string, 0) if torrent.Resolution > 0 { info = append(info, bittorrent.Resolutions[torrent.Resolution]) } if torrent.Size != "" { info = append(info, fmt.Sprintf("[%s]", torrent.Size)) } if torrent.RipType > 0 { info = append(info, bittorrent.Rips[torrent.RipType]) } if torrent.VideoCodec > 0 { info = append(info, bittorrent.Codecs[torrent.VideoCodec]) } if torrent.AudioCodec > 0 { info = append(info, bittorrent.Codecs[torrent.AudioCodec]) } if torrent.Provider != "" { info = append(info, " - "+torrent.Provider) } torrentName := torrent.Name if len(torrentName) > 80 { torrentName = torrentName[:80] } label := fmt.Sprintf("[B](%d / %d) %s[/B]\n%s", torrent.Seeds, torrent.Peers, strings.Join(info, " "), torrentName, ) choices = append(choices, label) } choice := xbmc.ListDialogLarge("LOCALIZE[30228]", longName, choices...) if choice >= 0 { rUrl := UrlQuery(UrlForXBMC("/play"), "uri", torrents[choice].Magnet()) ctx.Redirect(302, rUrl) } }
func ListEntities(endpoint string, params napping.Params) []*Entity { var wg sync.WaitGroup entities := make([]*Entity, MaxPages*resultsPerPage) params["api_key"] = apiKey params["language"] = "en" wg.Add(MaxPages) for i := 0; i < MaxPages; i++ { go func(page int) { defer wg.Done() var tmp *EntityList tmpParams := napping.Params{ "page": strconv.Itoa(startPage + page), } for k, v := range params { tmpParams[k] = v } urlValues := tmpParams.AsUrlValues() rateLimiter.Call(func() { resp, err := napping.Get( tmdbEndpoint+endpoint, &urlValues, &tmp, nil, ) if err != nil { log.Error(err.Error()) xbmc.Notify("Quasar", "ListEntities failed, check your logs.", config.AddonIcon()) } else if resp.Status() != 200 { message := fmt.Sprintf("ListEntities bad status: %d", resp.Status()) log.Error(message) xbmc.Notify("Quasar", message, config.AddonIcon()) } }) for i, entity := range tmp.Results { entities[page*resultsPerPage+i] = entity } }(i) } wg.Wait() return entities }
func MoviePlay(ctx *gin.Context) { torrents := movieLinks(ctx.Params.ByName("imdbId")) if len(torrents) == 0 { xbmc.Notify("Quasar", "LOCALIZE[30205]", config.AddonIcon()) return } sort.Sort(sort.Reverse(providers.ByQuality(torrents))) rUrl := UrlQuery(UrlForXBMC("/play"), "uri", torrents[0].Magnet()) ctx.Redirect(302, rUrl) }
func Scrobble(action string, contentType string, tmdbId int, runtime int) { if err := Authorized(); err != nil { return } if action != "update" { log.Notice(action, contentType, tmdbId) } retVal := xbmc.GetWatchTimes() errStr := retVal["error"] watchedTime, _ := strconv.ParseFloat(retVal["watchedTime"], 64) videoDuration, _ := strconv.ParseFloat(retVal["videoDuration"], 64) if errStr != "" { log.Warning(errStr) } else { scrobbleTime = watchedTime scrobbleEnd = videoDuration } if action == "update" { return } if scrobbleEnd == 0 { if runtime != 0 { scrobbleEnd = float64(runtime) log.Warningf("Using specified runtime of %d", runtime) } else { if contentType == "movie" { scrobbleEnd = 7200 } else { scrobbleEnd = 2700 } log.Warningf("Using fallback runtime of %d", videoDuration) } } progress := scrobbleTime / math.Floor(scrobbleEnd) * 100 log.Infof("Progress: %f%%, watched: %fs, duration: %fs", progress, scrobbleTime, scrobbleEnd) if action == "stop" { scrobbleTime = 0 scrobbleEnd = 0 } endPoint := fmt.Sprintf("scrobble/%s", action) resp, err := Post(endPoint, bytes.NewBufferString(fmt.Sprintf(`{"%s": {"ids": {"tmdb": %d}}, "progress": %f}`, contentType, tmdbId, progress))) if err != nil { log.Error(err.Error()) xbmc.Notify("Quasar", "Scrobble failed, check your logs.", config.AddonIcon()) } else if resp.Status() != 201 { log.Errorf("Failed to scrobble %s #%d to %s at %f: %d", contentType, tmdbId, action, progress, resp.Status()) } }