diff --git a/couchpotato/static/scripts/combined.plugins.min.js b/couchpotato/static/scripts/combined.plugins.min.js
index fa0bf8d..c54529b 100644
--- a/couchpotato/static/scripts/combined.plugins.min.js
+++ b/couchpotato/static/scripts/combined.plugins.min.js
@@ -176,6 +176,185 @@ window.addEvent("domready", function() {
new PutIODownloader();
});
+var QualityBase = new Class({
+ tab: "",
+ content: "",
+ setup: function(data) {
+ var self = this;
+ self.qualities = data.qualities;
+ self.profiles_list = null;
+ self.profiles = [];
+ Array.each(data.profiles, self.createProfilesClass.bind(self));
+ App.addEvent("loadSettings", self.addSettings.bind(self));
+ },
+ getProfile: function(id) {
+ return this.profiles.filter(function(profile) {
+ return profile.data._id == id;
+ }).pick();
+ },
+ getActiveProfiles: function() {
+ return Array.filter(this.profiles, function(profile) {
+ return !profile.data.hide;
+ });
+ },
+ getQuality: function(identifier) {
+ try {
+ return this.qualities.filter(function(q) {
+ return q.identifier == identifier;
+ }).pick();
+ } catch (e) {}
+ return {};
+ },
+ addSettings: function() {
+ var self = this;
+ self.settings = App.getPage("Settings");
+ self.settings.addEvent("create", function() {
+ var tab = self.settings.createSubTab("profile", {
+ label: "Quality",
+ name: "profile",
+ subtab_label: "Qualities"
+ }, self.settings.tabs.searcher, "searcher");
+ self.tab = tab.tab;
+ self.content = tab.content;
+ self.createProfiles();
+ self.createProfileOrdering();
+ self.createSizes();
+ });
+ },
+ createProfiles: function() {
+ var self = this;
+ var non_core_profiles = Array.filter(self.profiles, function(profile) {
+ return !profile.isCore();
+ });
+ var count = non_core_profiles.length;
+ self.settings.createGroup({
+ label: "Quality Profiles",
+ description: "Create your own profiles with multiple qualities."
+ }).inject(self.content).adopt(self.profile_container = new Element("div.container"), new Element("a.add_new_profile", {
+ text: count > 0 ? "Create another quality profile" : "Click here to create a quality profile.",
+ events: {
+ click: function() {
+ var profile = self.createProfilesClass();
+ $(profile).inject(self.profile_container);
+ }
+ }
+ }));
+ Array.each(non_core_profiles, function(profile) {
+ $(profile).inject(self.profile_container);
+ });
+ },
+ createProfilesClass: function(data) {
+ var self = this;
+ data = data || {
+ id: randomString()
+ };
+ var profile = new Profile(data);
+ self.profiles.include(profile);
+ return profile;
+ },
+ createProfileOrdering: function() {
+ var self = this;
+ self.settings.createGroup({
+ label: "Profile Defaults",
+ description: "(Needs refresh '" + (App.isMac() ? "CMD+R" : "F5") + "' after editing)"
+ }).grab(new Element(".ctrlHolder#profile_ordering").adopt(new Element("label[text=Order]"), self.profiles_list = new Element("ul"), new Element("p.formHint", {
+ html: "Change the order the profiles are in the dropdown list. Uncheck to hide it completely.
First one will be default."
+ }))).inject(self.content);
+ Array.each(self.profiles, function(profile) {
+ var check;
+ new Element("li", {
+ "data-id": profile.data._id
+ }).adopt(check = new Element("input[type=checkbox]", {
+ checked: !profile.data.hide,
+ events: {
+ change: self.saveProfileOrdering.bind(self)
+ }
+ }), new Element("span.profile_label", {
+ text: profile.data.label
+ }), new Element("span.handle.icon-handle")).inject(self.profiles_list);
+ });
+ var sorted_changed = false;
+ self.profile_sortable = new Sortables(self.profiles_list, {
+ revert: true,
+ handle: ".handle",
+ opacity: .5,
+ onSort: function() {
+ sorted_changed = true;
+ },
+ onComplete: function() {
+ if (sorted_changed) {
+ self.saveProfileOrdering();
+ sorted_changed = false;
+ }
+ }
+ });
+ },
+ saveProfileOrdering: function() {
+ var self = this, ids = [], hidden = [];
+ self.profiles_list.getElements("li").each(function(el, nr) {
+ ids.include(el.get("data-id"));
+ hidden[nr] = +!el.getElement("input[type=checkbox]").get("checked");
+ });
+ Api.request("profile.save_order", {
+ data: {
+ ids: ids,
+ hidden: hidden
+ }
+ });
+ },
+ createSizes: function() {
+ var self = this;
+ var group = self.settings.createGroup({
+ label: "Sizes",
+ description: "Edit the minimal and maximum sizes (in MB) for each quality.",
+ advanced: true,
+ name: "sizes"
+ }).inject(self.content);
+ new Element("div.item.head.ctrlHolder").adopt(new Element("span.label", {
+ text: "Quality"
+ }), new Element("span.min", {
+ text: "Min"
+ }), new Element("span.max", {
+ text: "Max"
+ })).inject(group);
+ Array.each(self.qualities, function(quality) {
+ new Element("div.ctrlHolder.item").adopt(new Element("span.label", {
+ text: quality.label
+ }), new Element("input.min[type=text]", {
+ value: quality.size_min,
+ events: {
+ keyup: function(e) {
+ self.changeSize(quality.identifier, "size_min", e.target.get("value"));
+ }
+ }
+ }), new Element("input.max[type=text]", {
+ value: quality.size_max,
+ events: {
+ keyup: function(e) {
+ self.changeSize(quality.identifier, "size_max", e.target.get("value"));
+ }
+ }
+ })).inject(group);
+ });
+ },
+ size_timer: {},
+ changeSize: function(identifier, type, value) {
+ var self = this;
+ if (self.size_timer[identifier + type]) clearTimeout(self.size_timer[identifier + type]);
+ self.size_timer[identifier + type] = function() {
+ Api.request("quality.size.save", {
+ data: {
+ identifier: identifier,
+ value_type: type,
+ value: value
+ }
+ });
+ }.delay(300);
+ }
+});
+
+window.Quality = new QualityBase();
+
var BlockSearch = new Class({
Extends: BlockBase,
cache: {},
@@ -2459,85 +2638,1552 @@ var TraktAutomation = new Class({
new TraktAutomation();
-var NotificationBase = new Class({
- Extends: BlockBase,
- Implements: [ Options, Events ],
- initialize: function(options) {
- var self = this;
- self.setOptions(options);
- App.addEvent("unload", self.stopPoll.bind(self));
- App.addEvent("reload", self.startInterval.bind(self, [ true ]));
- App.on("notification", self.notify.bind(self));
- App.on("message", self.showMessage.bind(self));
- App.addEvent("loadSettings", self.addTestButtons.bind(self));
- self.notifications = [];
- App.addEvent("load", function() {
- App.block.notification = new BlockMenu(self, {
- button_class: "icon-notifications",
- class: "notification_menu",
- onOpen: self.markAsRead.bind(self)
+Page.Shows = new Class({
+ Extends: PageBase,
+ name: "shows",
+ title: "List of TV Shows subscribed to",
+ folder_browser: null,
+ has_tab: false,
+ toggleShows: function(arg) {
+ var self = this;
+ var nav = App.getBlock("navigation");
+ if (typeof arg === "object" && arg.data === true || arg === true) {
+ self.tab = nav.addTab(self.name, {
+ href: App.createUrl(self.name),
+ title: self.title,
+ text: self.name.capitalize()
});
- $(App.block.notification).inject(App.getBlock("search"), "after");
- self.badge = new Element("div.badge").inject(App.block.notification, "top").hide();
- });
- window.addEvent("load", function() {
- self.startInterval.delay($(window).getSize().x <= 480 ? 2e3 : 100, self);
- });
- },
- notify: function(result) {
- var self = this;
- var added = new Date();
- added.setTime(result.added * 1e3);
- result.el = App.getBlock("notification").addLink(new Element("span." + (result.read ? "read" : "")).adopt(new Element("span.message", {
- html: result.message
- }), new Element("span.added", {
- text: added.timeDiffInWords(),
- title: added
- })), "top");
- self.notifications.include(result);
- if ((result.important !== undefined || result.sticky !== undefined) && !result.read) {
- var sticky = true;
- App.trigger("message", [ result.message, sticky, result ]);
- } else if (!result.read) {
- self.setBadge(self.notifications.filter(function(n) {
- return !n.read;
- }).length);
+ self.has_tab = true;
+ } else {
+ self.has_tab = false;
+ self.tab = null;
+ nav.removeTab("shows");
}
},
- setBadge: function(value) {
+ load: function() {
var self = this;
- self.badge.set("text", value);
- self.badge[value ? "show" : "hide"]();
- },
- markAsRead: function(force_ids) {
- var self = this, ids = force_ids;
- if (!force_ids) {
- var rn = self.notifications.filter(function(n) {
- return !n.read && n.important === undefined;
- });
- ids = [];
- rn.each(function(n) {
- ids.include(n._id);
- });
- }
- if (ids.length > 0) Api.request("notification.markread", {
- data: {
- ids: ids.join(",")
- },
- onSuccess: function() {
- self.setBadge("");
+ Api.request("settings", {
+ onComplete: function(json) {
+ self.toggleShows(json.values.shows.enabled);
}
});
+ App.on("shows.enabled", self.toggleShows.bind(self));
},
- startInterval: function(force) {
+ indexAction: function() {
var self = this;
- if (self.stopped && !force) {
- self.stopped = false;
- return;
- }
- self.request = Api.request("notification.listener", {
- data: {
- init: true
+ if (!self.wanted) {
+ self.wanted = new ShowList({
+ identifier: "wanted",
+ status: "active",
+ type: "show",
+ actions: [ MA.IMDB, MA.Trailer, MA.Release, MA.Edit, MA.Refresh, MA.Readd, MA.Delete ],
+ add_new: true,
+ on_empty_element: App.createUserscriptButtons().addClass("empty_wanted")
+ });
+ $(self.wanted).inject(self.el);
+ }
+ }
+});
+
+var EpisodeAction = new Class({
+ Implements: [ Options ],
+ class_name: "item-action icon2",
+ initialize: function(episode, options) {
+ var self = this;
+ self.setOptions(options);
+ self.show = episode.show;
+ self.episode = episode;
+ self.create();
+ if (self.el) self.el.addClass(self.class_name);
+ },
+ create: function() {},
+ disable: function() {
+ if (this.el) this.el.addClass("disable");
+ },
+ enable: function() {
+ if (this.el) this.el.removeClass("disable");
+ },
+ getTitle: function() {
+ var self = this;
+ try {
+ return self.show.getTitle();
+ } catch (e) {
+ try {
+ return self.show.original_title ? self.show.original_title : self.show.titles[0];
+ } catch (e) {
+ return "Unknown";
+ }
+ }
+ },
+ get: function(key) {
+ var self = this;
+ try {
+ return self.show.get(key);
+ } catch (e) {
+ return self.show[key];
+ }
+ },
+ createMask: function() {
+ var self = this;
+ self.mask = new Element("div.mask", {
+ styles: {
+ "z-index": "1"
+ }
+ }).inject(self.show, "top").fade("hide");
+ },
+ toElement: function() {
+ return this.el || null;
+ }
+});
+
+var EA = {};
+
+EA.IMDB = new Class({
+ Extends: EpisodeAction,
+ id: null,
+ create: function() {
+ var self = this;
+ self.id = self.show.getIdentifier ? self.show.getIdentifier() : self.get("imdb");
+ self.el = new Element("a.imdb", {
+ title: "Go to the IMDB page of " + self.getTitle(),
+ href: "http://www.imdb.com/title/" + self.id + "/",
+ target: "_blank"
+ });
+ if (!self.id) self.disable();
+ }
+});
+
+EA.Release = new Class({
+ Extends: EpisodeAction,
+ create: function() {
+ var self = this;
+ self.el = new Element("a.releases.download", {
+ title: "Show the releases that are available for " + self.getTitle(),
+ events: {
+ click: self.toggle.bind(self)
+ }
+ });
+ self.options = new Element("div.episode-options").inject(self.episode.el);
+ if (!self.episode.data.releases || self.episode.data.releases.length == 0) self.el.hide(); else self.showHelper();
+ App.on("show.searcher.ended", function(notification) {
+ if (self.show.data._id != notification.data._id) return;
+ self.releases = null;
+ if (self.options_container) {
+ self.options_container.destroy();
+ self.options_container = null;
+ }
+ });
+ },
+ toggle: function(e) {
+ var self = this;
+ if (self.options && self.options.hasClass("expanded")) {
+ self.close();
+ } else {
+ self.open();
+ }
+ },
+ open: function(e) {
+ var self = this;
+ if (e) e.preventDefault();
+ self.createReleases();
+ },
+ close: function(e) {
+ var self = this;
+ if (e) e.preventDefault();
+ self.options.setStyle("height", 0).removeClass("expanded");
+ },
+ createReleases: function() {
+ var self = this;
+ if (!self.releases_table) {
+ self.options.adopt(self.releases_table = new Element("div.releases.table"));
+ new Element("div.item.head").adopt(new Element("span.name", {
+ text: "Release name"
+ }), new Element("span.status", {
+ text: "Status"
+ }), new Element("span.quality", {
+ text: "Quality"
+ }), new Element("span.size", {
+ text: "Size"
+ }), new Element("span.age", {
+ text: "Age"
+ }), new Element("span.score", {
+ text: "Score"
+ }), new Element("span.provider", {
+ text: "Provider"
+ })).inject(self.releases_table);
+ if (self.episode.data.releases) self.episode.data.releases.each(function(release) {
+ var quality = Quality.getQuality(release.quality) || {}, info = release.info || {}, provider = self.get(release, "provider") + (info["provider_extra"] ? self.get(release, "provider_extra") : "");
+ var release_name = self.get(release, "name");
+ if (release.files && release.files.length > 0) {
+ try {
+ var movie_file = release.files.filter(function(file) {
+ var type = File.Type.get(file.type_id);
+ return type && type.identifier == "movie";
+ }).pick();
+ release_name = movie_file.path.split(Api.getOption("path_sep")).getLast();
+ } catch (e) {}
+ }
+ release["el"] = new Element("div", {
+ class: "item " + release.status,
+ id: "release_" + release._id
+ }).adopt(new Element("span.name", {
+ text: release_name,
+ title: release_name
+ }), new Element("span.status", {
+ text: release.status,
+ class: "status " + release.status
+ }), new Element("span.quality", {
+ text: quality.label + (release.is_3d ? " 3D" : "") || "n/a"
+ }), new Element("span.size", {
+ text: info["size"] ? Math.floor(self.get(release, "size")) : "n/a"
+ }), new Element("span.age", {
+ text: self.get(release, "age")
+ }), new Element("span.score", {
+ text: self.get(release, "score")
+ }), new Element("span.provider", {
+ text: provider,
+ title: provider
+ }), info["detail_url"] ? new Element("a.info.icon2", {
+ href: info["detail_url"],
+ target: "_blank"
+ }) : new Element("a"), new Element("a.download.icon2", {
+ events: {
+ click: function(e) {
+ e.preventDefault();
+ if (!this.hasClass("completed")) self.download(release);
+ }
+ }
+ }), new Element("a.delete.icon2", {
+ events: {
+ click: function(e) {
+ e.preventDefault();
+ self.ignore(release);
+ }
+ }
+ })).inject(self.releases_table);
+ if (release.status == "ignored" || release.status == "failed" || release.status == "snatched") {
+ if (!self.last_release || self.last_release && self.last_release.status != "snatched" && release.status == "snatched") self.last_release = release;
+ } else if (!self.next_release && release.status == "available") {
+ self.next_release = release;
+ }
+ var update_handle = function(notification) {
+ if (notification.data._id != release._id) return;
+ var q = self.show.quality.getElement(".q_" + release.quality), new_status = notification.data.status;
+ release.el.set("class", "item " + new_status);
+ var status_el = release.el.getElement(".release_status");
+ status_el.set("class", "release_status " + new_status);
+ status_el.set("text", new_status);
+ if (!q && (new_status == "snatched" || new_status == "seeding" || new_status == "done")) q = self.addQuality(release.quality_id);
+ if (q && !q.hasClass(new_status)) {
+ q.removeClass(release.status).addClass(new_status);
+ q.set("title", q.get("title").replace(release.status, new_status));
+ }
+ };
+ App.on("release.update_status", update_handle);
+ });
+ if (self.last_release) self.releases_table.getElements("#release_" + self.last_release._id).addClass("last_release");
+ if (self.next_release) self.releases_table.getElements("#release_" + self.next_release._id).addClass("next_release");
+ if (self.next_release || self.last_release && [ "ignored", "failed" ].indexOf(self.last_release.status) === false) {
+ self.trynext_container = new Element("div.buttons.try_container").inject(self.releases_table, "top");
+ var nr = self.next_release, lr = self.last_release;
+ self.trynext_container.adopt(new Element("span.or", {
+ text: "If anything went wrong, download"
+ }), lr ? new Element("a.button.orange", {
+ text: "the same release again",
+ events: {
+ click: function() {
+ self.download(lr);
+ }
+ }
+ }) : null, nr && lr ? new Element("span.or", {
+ text: ","
+ }) : null, nr ? [ new Element("a.button.green", {
+ text: lr ? "another release" : "the best release",
+ events: {
+ click: function() {
+ self.download(nr);
+ }
+ }
+ }), new Element("span.or", {
+ text: "or pick one below"
+ }) ] : null);
+ }
+ self.last_release = null;
+ self.next_release = null;
+ self.episode.el.addEvent("outerClick", function() {
+ self.close();
+ });
+ }
+ self.options.setStyle("height", self.releases_table.getSize().y).addClass("expanded");
+ },
+ showHelper: function(e) {
+ var self = this;
+ if (e) e.preventDefault();
+ var has_available = false, has_snatched = false;
+ if (self.episode.data.releases) self.episode.data.releases.each(function(release) {
+ if (has_available && has_snatched) return;
+ if ([ "snatched", "downloaded", "seeding" ].contains(release.status)) has_snatched = true;
+ if ([ "available" ].contains(release.status)) has_available = true;
+ });
+ if (has_available || has_snatched) {
+ self.trynext_container = new Element("div.buttons.trynext").inject(self.show.info_container);
+ self.trynext_container.adopt(has_available ? [ new Element("a.icon2.readd", {
+ text: has_snatched ? "Download another release" : "Download the best release",
+ events: {
+ click: self.tryNextRelease.bind(self)
+ }
+ }), new Element("a.icon2.download", {
+ text: "pick one yourself",
+ events: {
+ click: function() {
+ self.show.quality.fireEvent("click");
+ }
+ }
+ }) ] : null, new Element("a.icon2.completed", {
+ text: "mark this movie done",
+ events: {
+ click: self.markMovieDone.bind(self)
+ }
+ }));
+ }
+ },
+ get: function(release, type) {
+ return release.info && release.info[type] !== undefined ? release.info[type] : "n/a";
+ },
+ download: function(release) {
+ var self = this;
+ var release_el = self.releases_table.getElement("#release_" + release._id), icon = release_el.getElement(".download.icon2");
+ if (icon) icon.addClass("icon spinner").removeClass("download");
+ Api.request("release.manual_download", {
+ data: {
+ id: release._id
+ },
+ onComplete: function(json) {
+ if (icon) icon.removeClass("icon spinner");
+ if (json.success) {
+ if (icon) icon.addClass("completed");
+ release_el.getElement(".release_status").set("text", "snatched");
+ } else if (icon) icon.addClass("attention").set("title", "Something went wrong when downloading, please check logs.");
+ }
+ });
+ },
+ ignore: function(release) {
+ Api.request("release.ignore", {
+ data: {
+ id: release._id
+ }
+ });
+ },
+ markMovieDone: function() {
+ var self = this;
+ Api.request("media.delete", {
+ data: {
+ id: self.show.get("_id"),
+ delete_from: "wanted"
+ },
+ onComplete: function() {
+ var movie = $(self.show);
+ movie.set("tween", {
+ duration: 300,
+ onComplete: function() {
+ self.show.destroy();
+ }
+ });
+ movie.tween("height", 0);
+ }
+ });
+ },
+ tryNextRelease: function() {
+ var self = this;
+ Api.request("movie.searcher.try_next", {
+ data: {
+ media_id: self.show.get("_id")
+ }
+ });
+ }
+});
+
+EA.Refresh = new Class({
+ Extends: EpisodeAction,
+ create: function() {
+ var self = this;
+ self.el = new Element("a.refresh", {
+ title: "Refresh the movie info and do a forced search",
+ events: {
+ click: self.doRefresh.bind(self)
+ }
+ });
+ },
+ doRefresh: function(e) {
+ var self = this;
+ e.preventDefault();
+ Api.request("media.refresh", {
+ data: {
+ id: self.episode.get("_id")
+ }
+ });
+ }
+});
+
+var Episode = new Class({
+ Extends: BlockBase,
+ action: {},
+ initialize: function(show, options, data) {
+ var self = this;
+ self.setOptions(options);
+ self.show = show;
+ self.options = options;
+ self.data = data;
+ self.profile = self.show.profile;
+ self.el = new Element("div.item.episode").adopt(self.detail = new Element("div.item.data"));
+ self.create();
+ },
+ create: function() {
+ var self = this;
+ self.detail.set("id", "episode_" + self.data._id);
+ self.detail.adopt(new Element("span.episode", {
+ text: self.data.info.number || 0
+ }), new Element("span.name", {
+ text: self.getTitle()
+ }), new Element("span.firstaired", {
+ text: self.data.info.firstaired
+ }), self.quality = new Element("span.quality", {
+ events: {
+ click: function(e) {
+ var releases = self.detail.getElement(".item-actions .releases");
+ if (releases.isVisible()) releases.fireEvent("click", [ e ]);
+ }
+ }
+ }), self.actions = new Element("div.item-actions"));
+ if (self.profile.data) {
+ self.profile.getTypes().each(function(type) {
+ var q = self.addQuality(type.get("quality"), type.get("3d"));
+ if ((type.finish == true || type.get("finish")) && !q.hasClass("finish")) {
+ q.addClass("finish");
+ q.set("title", q.get("title") + " Will finish searching for this movie if this quality is found.");
+ }
+ });
+ }
+ self.updateReleases();
+ Object.each(self.options.actions, function(action, key) {
+ self.action[key.toLowerCase()] = action = new self.options.actions[key](self);
+ if (action.el) self.actions.adopt(action);
+ });
+ },
+ updateReleases: function() {
+ var self = this;
+ if (!self.data.releases || self.data.releases.length == 0) return;
+ self.data.releases.each(function(release) {
+ var q = self.quality.getElement(".q_" + release.quality + (release.is_3d ? ".is_3d" : ":not(.is_3d)")), status = release.status;
+ if (!q && (status == "snatched" || status == "seeding" || status == "done")) q = self.addQuality(release.quality, release.is_3d || false);
+ if (q && !q.hasClass(status)) {
+ q.addClass(status);
+ q.set("title", (q.get("title") ? q.get("title") : "") + " status: " + status);
+ }
+ });
+ },
+ addQuality: function(quality, is_3d) {
+ var self = this, q = Quality.getQuality(quality);
+ return new Element("span", {
+ text: q.label + (is_3d ? " 3D" : ""),
+ class: "q_" + q.identifier + (is_3d ? " is_3d" : ""),
+ title: ""
+ }).inject(self.quality);
+ },
+ getTitle: function() {
+ var self = this;
+ var title = "";
+ if (self.data.info.titles && self.data.info.titles.length > 0) {
+ title = self.data.info.titles[0];
+ } else {
+ title = "Episode " + self.data.info.number;
+ }
+ return title;
+ },
+ getIdentifier: function() {
+ var self = this;
+ try {
+ return self.get("identifiers").imdb;
+ } catch (e) {}
+ return self.get("imdb");
+ },
+ get: function(attr) {
+ return this.data[attr] || this.data.info[attr];
+ }
+});
+
+var ShowList = new Class({
+ Implements: [ Events, Options ],
+ options: {
+ navigation: true,
+ limit: 50,
+ load_more: true,
+ loader: true,
+ menu: [],
+ add_new: false,
+ force_view: false
+ },
+ movies: [],
+ movies_added: {},
+ total_movies: 0,
+ letters: {},
+ filter: null,
+ initialize: function(options) {
+ var self = this;
+ self.setOptions(options);
+ self.offset = 0;
+ self.filter = self.options.filter || {
+ starts_with: null,
+ search: null
+ };
+ self.el = new Element("div.shows").adopt(self.title = self.options.title ? new Element("h2", {
+ text: self.options.title,
+ styles: {
+ display: "none"
+ }
+ }) : null, self.description = self.options.description ? new Element("div.description", {
+ html: self.options.description,
+ styles: {
+ display: "none"
+ }
+ }) : null, self.movie_list = new Element("div.list"), self.load_more = self.options.load_more ? new Element("a.load_more", {
+ events: {
+ click: self.loadMore.bind(self)
+ }
+ }) : null);
+ if ($(window).getSize().x <= 480 && !self.options.force_view) self.changeView("list"); else self.changeView(self.getSavedView() || self.options.view || "details");
+ self.getMovies();
+ App.on("movie.added", self.movieAdded.bind(self));
+ App.on("movie.deleted", self.movieDeleted.bind(self));
+ },
+ movieDeleted: function(notification) {
+ var self = this;
+ if (self.movies_added[notification.data._id]) {
+ self.movies.each(function(movie) {
+ if (movie.get("_id") == notification.data._id) {
+ movie.destroy();
+ delete self.movies_added[notification.data._id];
+ self.setCounter(self.counter_count - 1);
+ self.total_movies--;
+ }
+ });
+ }
+ self.checkIfEmpty();
+ },
+ movieAdded: function(notification) {
+ var self = this;
+ self.fireEvent("movieAdded", notification);
+ if (self.options.add_new && !self.movies_added[notification.data._id] && notification.data.status == self.options.status) {
+ window.scroll(0, 0);
+ self.createShow(notification.data, "top");
+ self.setCounter(self.counter_count + 1);
+ self.checkIfEmpty();
+ }
+ },
+ create: function() {
+ var self = this;
+ if (self.options.navigation) self.createNavigation();
+ if (self.options.load_more) self.scrollspy = new ScrollSpy({
+ min: function() {
+ var c = self.load_more.getCoordinates();
+ return c.top - window.document.getSize().y - 300;
+ },
+ onEnter: self.loadMore.bind(self)
+ });
+ self.created = true;
+ },
+ addMovies: function(movies, total) {
+ var self = this;
+ if (!self.created) self.create();
+ if (movies.length < self.options.limit && self.scrollspy) {
+ self.load_more.hide();
+ self.scrollspy.stop();
+ }
+ Object.each(movies, function(movie) {
+ self.createShow(movie);
+ });
+ self.total_movies += total;
+ self.setCounter(total);
+ },
+ setCounter: function(count) {
+ var self = this;
+ if (!self.navigation_counter) return;
+ self.counter_count = count;
+ self.navigation_counter.set("text", (count || 0) + " shows");
+ if (self.empty_message) {
+ self.empty_message.destroy();
+ self.empty_message = null;
+ }
+ if (self.total_movies && count == 0 && !self.empty_message) {
+ var message = (self.filter.search ? 'for "' + self.filter.search + '"' : "") + (self.filter.starts_with ? " in " + self.filter.starts_with + "" : "");
+ self.empty_message = new Element(".message", {
+ html: "No shows found " + message + ".
"
+ }).grab(new Element("a", {
+ text: "Reset filter",
+ events: {
+ click: function() {
+ self.filter = {
+ starts_with: null,
+ search: null
+ };
+ self.navigation_search_input.set("value", "");
+ self.reset();
+ self.activateLetter();
+ self.getMovies(true);
+ self.last_search_value = "";
+ }
+ }
+ })).inject(self.movie_list);
+ }
+ },
+ createShow: function(show, inject_at) {
+ var self = this;
+ var m = new Show(self, {
+ actions: self.options.actions,
+ view: self.current_view,
+ onSelect: self.calculateSelected.bind(self)
+ }, show);
+ $(m).inject(self.movie_list, inject_at || "bottom");
+ m.fireEvent("injected");
+ self.movies.include(m);
+ self.movies_added[show._id] = true;
+ },
+ createNavigation: function() {
+ var self = this;
+ var chars = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ self.el.addClass("with_navigation");
+ self.navigation = new Element("div.alph_nav").adopt(self.mass_edit_form = new Element("div.mass_edit_form").adopt(new Element("span.select").adopt(self.mass_edit_select = new Element("input[type=checkbox].inlay", {
+ events: {
+ change: self.massEditToggleAll.bind(self)
+ }
+ }), self.mass_edit_selected = new Element("span.count", {
+ text: 0
+ }), self.mass_edit_selected_label = new Element("span", {
+ text: "selected"
+ })), new Element("div.quality").adopt(self.mass_edit_quality = new Element("select"), new Element("a.button.orange", {
+ text: "Change quality",
+ events: {
+ click: self.changeQualitySelected.bind(self)
+ }
+ })), new Element("div.delete").adopt(new Element("span[text=or]"), new Element("a.button.red", {
+ text: "Delete",
+ events: {
+ click: self.deleteSelected.bind(self)
+ }
+ })), new Element("div.refresh").adopt(new Element("span[text=or]"), new Element("a.button.green", {
+ text: "Refresh",
+ events: {
+ click: self.refreshSelected.bind(self)
+ }
+ }))), new Element("div.menus").adopt(self.navigation_counter = new Element("span.counter[title=Total]"), self.filter_menu = new Block.Menu(self, {
+ class: "filter"
+ }), self.navigation_actions = new Element("ul.actions", {
+ events: {
+ "click:relay(li)": function(e, el) {
+ var a = "active";
+ self.navigation_actions.getElements("." + a).removeClass(a);
+ self.changeView(el.get("data-view"));
+ this.addClass(a);
+ el.inject(el.getParent(), "top");
+ el.getSiblings().hide();
+ setTimeout(function() {
+ el.getSiblings().setStyle("display", null);
+ }, 100);
+ }
+ }
+ }), self.navigation_menu = new Block.Menu(self, {
+ class: "extra"
+ }))).inject(self.el, "top");
+ self.mass_edit_select_class = new Form.Check(self.mass_edit_select);
+ Quality.getActiveProfiles().each(function(profile) {
+ new Element("option", {
+ value: profile.get("_id"),
+ text: profile.get("label")
+ }).inject(self.mass_edit_quality);
+ });
+ self.filter_menu.addLink(self.navigation_search_input = new Element("input", {
+ title: "Search through " + self.options.identifier,
+ placeholder: "Search through " + self.options.identifier,
+ events: {
+ keyup: self.search.bind(self),
+ change: self.search.bind(self)
+ }
+ })).addClass("search");
+ var available_chars;
+ self.filter_menu.addEvent("open", function() {
+ self.navigation_search_input.focus();
+ if (!available_chars && (self.navigation.isDisplayed() || self.navigation.isVisible())) Api.request("media.available_chars", {
+ data: Object.merge({
+ type: "show",
+ status: self.options.status
+ }, self.filter),
+ onSuccess: function(json) {
+ available_chars = json.chars;
+ available_chars.each(function(c) {
+ self.letters[c.capitalize()].addClass("available");
+ });
+ }
+ });
+ });
+ self.filter_menu.addLink(self.navigation_alpha = new Element("ul.numbers", {
+ events: {
+ "click:relay(li.available)": function(e, el) {
+ self.activateLetter(el.get("data-letter"));
+ self.getMovies(true);
+ }
+ }
+ }));
+ [ "mass_edit", "details", "list" ].each(function(view) {
+ var current = self.current_view == view;
+ new Element("li", {
+ class: "icon2 " + view + (current ? " active " : ""),
+ "data-view": view
+ }).inject(self.navigation_actions, current ? "top" : "bottom");
+ });
+ self.letters["all"] = new Element("li.letter_all.available.active", {
+ text: "ALL"
+ }).inject(self.navigation_alpha);
+ chars.split("").each(function(c) {
+ self.letters[c] = new Element("li", {
+ text: c,
+ class: "letter_" + c,
+ "data-letter": c
+ }).inject(self.navigation_alpha);
+ });
+ if (self.options.menu.length > 0) self.options.menu.each(function(menu_item) {
+ self.navigation_menu.addLink(menu_item);
+ }); else self.navigation_menu.hide();
+ },
+ calculateSelected: function() {
+ var self = this;
+ var selected = 0, movies = self.movies.length;
+ self.movies.each(function(movie) {
+ selected += movie.isSelected() ? 1 : 0;
+ });
+ var indeterminate = selected > 0 && selected < movies, checked = selected == movies && selected > 0;
+ self.mass_edit_select.set("indeterminate", indeterminate);
+ self.mass_edit_select_class[checked ? "check" : "uncheck"]();
+ self.mass_edit_select_class.element[indeterminate ? "addClass" : "removeClass"]("indeterminate");
+ self.mass_edit_selected.set("text", selected);
+ },
+ deleteSelected: function() {
+ var self = this, ids = self.getSelectedMovies(), help_msg = self.identifier == "wanted" ? "If you do, you won't be able to watch them, as they won't get downloaded!" : "Your files will be safe, this will only delete the reference from the CouchPotato manage list";
+ var qObj = new Question("Are you sure you want to delete " + ids.length + " movie" + (ids.length != 1 ? "s" : "") + "?", help_msg, [ {
+ text: "Yes, delete " + (ids.length != 1 ? "them" : "it"),
+ class: "delete",
+ events: {
+ click: function(e) {
+ e.preventDefault();
+ this.set("text", "Deleting..");
+ Api.request("media.delete", {
+ method: "post",
+ data: {
+ id: ids.join(","),
+ delete_from: self.options.identifier
+ },
+ onSuccess: function() {
+ qObj.close();
+ var erase_movies = [];
+ self.movies.each(function(movie) {
+ if (movie.isSelected()) {
+ $(movie).destroy();
+ erase_movies.include(movie);
+ }
+ });
+ erase_movies.each(function(movie) {
+ self.movies.erase(movie);
+ movie.destroy();
+ self.setCounter(self.counter_count - 1);
+ self.total_movies--;
+ });
+ self.calculateSelected();
+ }
+ });
+ }
+ }
+ }, {
+ text: "Cancel",
+ cancel: true
+ } ]);
+ },
+ changeQualitySelected: function() {
+ var self = this;
+ var ids = self.getSelectedMovies();
+ Api.request("movie.edit", {
+ method: "post",
+ data: {
+ id: ids.join(","),
+ profile_id: self.mass_edit_quality.get("value")
+ },
+ onSuccess: self.search.bind(self)
+ });
+ },
+ refreshSelected: function() {
+ var self = this;
+ var ids = self.getSelectedMovies();
+ Api.request("media.refresh", {
+ method: "post",
+ data: {
+ id: ids.join(",")
+ }
+ });
+ },
+ getSelectedMovies: function() {
+ var self = this;
+ var ids = [];
+ self.movies.each(function(movie) {
+ if (movie.isSelected()) ids.include(movie.get("_id"));
+ });
+ return ids;
+ },
+ massEditToggleAll: function() {
+ var self = this;
+ var select = self.mass_edit_select.get("checked");
+ self.movies.each(function(movie) {
+ movie.select(select);
+ });
+ self.calculateSelected();
+ },
+ reset: function() {
+ var self = this;
+ self.movies = [];
+ if (self.mass_edit_select) self.calculateSelected();
+ if (self.navigation_alpha) self.navigation_alpha.getElements(".active").removeClass("active");
+ self.offset = 0;
+ if (self.scrollspy) {
+ self.scrollspy.start();
+ }
+ },
+ activateLetter: function(letter) {
+ var self = this;
+ self.reset();
+ self.letters[letter || "all"].addClass("active");
+ self.filter.starts_with = letter;
+ },
+ changeView: function(new_view) {
+ var self = this;
+ self.el.removeClass(self.current_view + "_list").addClass(new_view + "_list");
+ self.current_view = new_view;
+ Cookie.write(self.options.identifier + "_view2", new_view, {
+ duration: 1e3
+ });
+ },
+ getSavedView: function() {
+ var self = this;
+ return Cookie.read(self.options.identifier + "_view2");
+ },
+ search: function() {
+ var self = this;
+ if (self.search_timer) clearTimeout(self.search_timer);
+ self.search_timer = function() {
+ var search_value = self.navigation_search_input.get("value");
+ if (search_value == self.last_search_value) return;
+ self.reset();
+ self.activateLetter();
+ self.filter.search = search_value;
+ self.getMovies(true);
+ self.last_search_value = search_value;
+ }.delay(250);
+ },
+ update: function() {
+ var self = this;
+ self.reset();
+ self.getMovies(true);
+ },
+ getMovies: function(reset) {
+ var self = this;
+ if (self.scrollspy) {
+ self.scrollspy.stop();
+ self.load_more.set("text", "loading...");
+ }
+ if (self.movies.length == 0 && self.options.loader) {
+ self.loader_first = new Element("div.loading").adopt(new Element("div.message", {
+ text: self.options.title ? "Loading '" + self.options.title + "'" : "Loading..."
+ })).inject(self.el, "top");
+ createSpinner(self.loader_first, {
+ radius: 4,
+ length: 4,
+ width: 1
+ });
+ self.el.setStyle("min-height", 93);
+ }
+ Api.request(self.options.api_call || "media.list", {
+ data: Object.merge({
+ type: self.options.type || "movie",
+ status: self.options.status,
+ limit_offset: self.options.limit ? self.options.limit + "," + self.offset : null
+ }, self.filter),
+ onSuccess: function(json) {
+ if (reset) self.movie_list.empty();
+ if (self.loader_first) {
+ var lf = self.loader_first;
+ self.loader_first.addClass("hide");
+ self.loader_first = null;
+ setTimeout(function() {
+ lf.destroy();
+ }, 2e4);
+ self.el.setStyle("min-height", null);
+ }
+ self.store(json.shows);
+ self.addMovies(json.shows, json.total || json.shows.length);
+ if (self.scrollspy) {
+ self.load_more.set("text", "load more movies");
+ self.scrollspy.start();
+ }
+ self.checkIfEmpty();
+ self.fireEvent("loaded");
+ }
+ });
+ },
+ loadMore: function() {
+ var self = this;
+ if (self.offset >= self.options.limit) self.getMovies();
+ },
+ store: function(movies) {
+ var self = this;
+ self.offset += movies.length;
+ },
+ checkIfEmpty: function() {
+ var self = this;
+ var is_empty = self.movies.length == 0 && (self.total_movies == 0 || self.total_movies === undefined);
+ if (self.title) self.title[is_empty ? "hide" : "show"]();
+ if (self.description) self.description.setStyle("display", [ is_empty ? "none" : "" ]);
+ if (is_empty && self.options.on_empty_element) {
+ self.options.on_empty_element.inject(self.loader_first || self.title || self.movie_list, "after");
+ if (self.navigation) self.navigation.hide();
+ self.empty_element = self.options.on_empty_element;
+ } else if (self.empty_element) {
+ self.empty_element.destroy();
+ if (self.navigation) self.navigation.show();
+ }
+ },
+ toElement: function() {
+ return this.el;
+ }
+});
+
+Block.Search.ShowItem = new Class({
+ Implements: [ Options, Events ],
+ initialize: function(info, options) {
+ var self = this;
+ self.setOptions(options);
+ self.info = info;
+ self.alternative_titles = [];
+ self.create();
+ },
+ create: function() {
+ var self = this, info = self.info;
+ self.el = new Element("div.media_result", {
+ id: info.id
+ }).adopt(self.thumbnail = info.images && info.images.poster.length > 0 ? new Element("img.thumbnail", {
+ src: info.images.poster[0],
+ height: null,
+ width: null
+ }) : null, self.options_el = new Element("div.options.inlay"), self.data_container = new Element("div.data", {
+ events: {
+ click: self.showOptions.bind(self)
+ }
+ }).adopt(self.info_container = new Element("div.info").adopt(new Element("h2").adopt(self.title = new Element("span.title", {
+ text: info.titles && info.titles.length > 0 ? info.titles[0] : "Unknown"
+ }), self.year = info.year ? new Element("span.year", {
+ text: info.year
+ }) : null))));
+ if (info.titles) info.titles.each(function(title) {
+ self.alternativeTitle({
+ title: title
+ });
+ });
+ },
+ alternativeTitle: function(alternative) {
+ var self = this;
+ self.alternative_titles.include(alternative);
+ },
+ getTitle: function() {
+ var self = this;
+ try {
+ return self.info.original_title ? self.info.original_title : self.info.titles[0];
+ } catch (e) {
+ return "Unknown";
+ }
+ },
+ get: function(key) {
+ return this.info[key];
+ },
+ showOptions: function() {
+ var self = this;
+ self.createOptions();
+ self.data_container.addClass("open");
+ self.el.addEvent("outerClick", self.closeOptions.bind(self));
+ },
+ closeOptions: function() {
+ var self = this;
+ self.data_container.removeClass("open");
+ self.el.removeEvents("outerClick");
+ },
+ add: function(e) {
+ var self = this;
+ if (e) e.preventDefault();
+ self.loadingMask();
+ Api.request("show.add", {
+ data: {
+ identifiers: self.info.identifiers,
+ type: self.info.type,
+ title: self.title_select.get("value"),
+ profile_id: self.profile_select.get("value"),
+ category_id: self.category_select.get("value")
+ },
+ onComplete: function(json) {
+ self.options_el.empty();
+ self.options_el.adopt(new Element("div.message", {
+ text: json.success ? "Show successfully added." : "Show didn't add properly. Check logs"
+ }));
+ self.mask.fade("out");
+ self.fireEvent("added");
+ },
+ onFailure: function() {
+ self.options_el.empty();
+ self.options_el.adopt(new Element("div.message", {
+ text: "Something went wrong, check the logs for more info."
+ }));
+ self.mask.fade("out");
+ }
+ });
+ },
+ createOptions: function() {
+ var self = this, info = self.info;
+ if (!self.options_el.hasClass("set")) {
+ if (self.info.in_library) {
+ var in_library = [];
+ self.info.in_library.releases.each(function(release) {
+ in_library.include(release.quality.label);
+ });
+ }
+ self.options_el.grab(new Element("div", {
+ class: self.info.in_wanted && self.info.in_wanted.profile_id || in_library ? "in_library_wanted" : ""
+ }).adopt(self.info.in_wanted && self.info.in_wanted.profile_id ? new Element("span.in_wanted", {
+ text: "Already in wanted list: " + Quality.getProfile(self.info.in_wanted.profile_id).get("label")
+ }) : in_library ? new Element("span.in_library", {
+ text: "Already in library: " + in_library.join(", ")
+ }) : null, self.title_select = new Element("select", {
+ name: "title"
+ }), self.profile_select = new Element("select", {
+ name: "profile"
+ }), self.category_select = new Element("select", {
+ name: "category"
+ }).grab(new Element("option", {
+ value: -1,
+ text: "None"
+ })), self.add_button = new Element("a.button", {
+ text: "Add",
+ events: {
+ click: self.add.bind(self)
+ }
+ })));
+ Array.each(self.alternative_titles, function(alt) {
+ new Element("option", {
+ text: alt.title
+ }).inject(self.title_select);
+ });
+ var categories = CategoryList.getAll();
+ if (categories.length == 0) self.category_select.hide(); else {
+ self.category_select.show();
+ categories.each(function(category) {
+ new Element("option", {
+ value: category.data._id,
+ text: category.data.label
+ }).inject(self.category_select);
+ });
+ }
+ var profiles = Quality.getActiveProfiles();
+ if (profiles.length == 1) self.profile_select.hide();
+ profiles.each(function(profile) {
+ new Element("option", {
+ value: profile.get("_id"),
+ text: profile.get("label")
+ }).inject(self.profile_select);
+ });
+ self.options_el.addClass("set");
+ if (categories.length == 0 && self.title_select.getElements("option").length == 1 && profiles.length == 1 && !(self.info.in_wanted && self.info.in_wanted.profile_id || in_library)) self.add();
+ }
+ },
+ loadingMask: function() {
+ var self = this;
+ self.mask = new Element("div.mask").inject(self.el).fade("hide");
+ createSpinner(self.mask);
+ self.mask.fade("in");
+ },
+ toElement: function() {
+ return this.el;
+ }
+});
+
+var Season = new Class({
+ Extends: BlockBase,
+ action: {},
+ initialize: function(show, options, data) {
+ var self = this;
+ self.setOptions(options);
+ self.show = show;
+ self.options = options;
+ self.data = data;
+ self.profile = self.show.profile;
+ self.el = new Element("div.item.season").adopt(self.detail = new Element("div.item.data"));
+ self.create();
+ },
+ create: function() {
+ var self = this;
+ self.detail.set("id", "season_" + self.data._id);
+ self.detail.adopt(new Element("span.name", {
+ text: self.getTitle()
+ }), self.quality = new Element("span.quality", {
+ events: {
+ click: function(e) {
+ var releases = self.detail.getElement(".item-actions .releases");
+ if (releases.isVisible()) releases.fireEvent("click", [ e ]);
+ }
+ }
+ }), self.actions = new Element("div.item-actions"));
+ if (self.profile.data) {
+ self.profile.getTypes().each(function(type) {
+ var q = self.addQuality(type.get("quality"), type.get("3d"));
+ if ((type.finish == true || type.get("finish")) && !q.hasClass("finish")) {
+ q.addClass("finish");
+ q.set("title", q.get("title") + " Will finish searching for this movie if this quality is found.");
+ }
+ });
+ }
+ self.updateReleases();
+ Object.each(self.options.actions, function(action, key) {
+ self.action[key.toLowerCase()] = action = new self.options.actions[key](self);
+ if (action.el) self.actions.adopt(action);
+ });
+ },
+ updateReleases: function() {
+ var self = this;
+ if (!self.data.releases || self.data.releases.length == 0) return;
+ self.data.releases.each(function(release) {
+ var q = self.quality.getElement(".q_" + release.quality + (release.is_3d ? ".is_3d" : ":not(.is_3d)")), status = release.status;
+ if (!q && (status == "snatched" || status == "seeding" || status == "done")) q = self.addQuality(release.quality, release.is_3d || false);
+ if (q && !q.hasClass(status)) {
+ q.addClass(status);
+ q.set("title", (q.get("title") ? q.get("title") : "") + " status: " + status);
+ }
+ });
+ },
+ addQuality: function(quality, is_3d) {
+ var self = this, q = Quality.getQuality(quality);
+ return new Element("span", {
+ text: q.label + (is_3d ? " 3D" : ""),
+ class: "q_" + q.identifier + (is_3d ? " is_3d" : ""),
+ title: ""
+ }).inject(self.quality);
+ },
+ getTitle: function() {
+ var self = this;
+ var title = "";
+ if (self.data.info.number) {
+ title = "Season " + self.data.info.number;
+ } else {
+ title = "Specials";
+ }
+ return title;
+ },
+ getIdentifier: function() {
+ var self = this;
+ try {
+ return self.get("identifiers").imdb;
+ } catch (e) {}
+ return self.get("imdb");
+ },
+ get: function(attr) {
+ return this.data[attr] || this.data.info[attr];
+ }
+});
+
+var Episodes = new Class({
+ initialize: function(show, options) {
+ var self = this;
+ self.show = show;
+ self.options = options;
+ },
+ open: function() {
+ var self = this;
+ if (!self.container) {
+ self.container = new Element("div.options").grab(self.episodes_container = new Element("div.episodes.table"));
+ self.container.inject(self.show, "top");
+ Api.request("library.tree", {
+ data: {
+ media_id: self.show.data._id
+ },
+ onComplete: function(json) {
+ self.data = json.result;
+ self.createEpisodes();
+ }
+ });
+ }
+ self.show.slide("in", self.container, true);
+ },
+ createEpisodes: function() {
+ var self = this;
+ self.data.seasons.sort(self.sortSeasons);
+ self.data.seasons.each(function(season) {
+ self.createSeason(season);
+ season.episodes.sort(self.sortEpisodes);
+ season.episodes.each(function(episode) {
+ self.createEpisode(episode);
+ });
+ });
+ },
+ createSeason: function(season) {
+ var self = this, s = new Season(self.show, self.options, season);
+ $(s).inject(self.episodes_container);
+ },
+ createEpisode: function(episode) {
+ var self = this, e = new Episode(self.show, self.options, episode);
+ $(e).inject(self.episodes_container);
+ },
+ sortSeasons: function(a, b) {
+ if (!a.info.number) {
+ return 1;
+ }
+ if (!b.info.number) {
+ return -1;
+ }
+ if (a.info.number < b.info.number) return -1;
+ if (a.info.number > b.info.number) return 1;
+ return 0;
+ },
+ sortEpisodes: function(a, b) {
+ if (a.info.number < b.info.number) return -1;
+ if (a.info.number > b.info.number) return 1;
+ return 0;
+ }
+});
+
+var Show = new Class({
+ Extends: BlockBase,
+ action: {},
+ initialize: function(list, options, data) {
+ var self = this;
+ self.data = data;
+ self.view = options.view || "details";
+ self.list = list;
+ self.el = new Element("div.show");
+ self.episodes = new Episodes(self, {
+ actions: [ EA.IMDB, EA.Release, EA.Refresh ]
+ });
+ self.profile = Quality.getProfile(data.profile_id) || {};
+ self.category = CategoryList.getCategory(data.category_id) || {};
+ self.parent(self, options);
+ self.addEvents();
+ },
+ addEvents: function() {
+ var self = this;
+ self.global_events = {};
+ self.global_events["movie.update"] = function(notification) {
+ if (self.data._id != notification.data._id) return;
+ self.busy(false);
+ self.removeView();
+ self.update.delay(2e3, self, notification);
+ };
+ App.on("movie.update", self.global_events["movie.update"]);
+ [ "media.busy", "movie.searcher.started" ].each(function(listener) {
+ self.global_events[listener] = function(notification) {
+ if (notification.data && (self.data._id == notification.data._id || typeOf(notification.data._id) == "array" && notification.data._id.indexOf(self.data._id) > -1)) self.busy(true);
+ };
+ App.on(listener, self.global_events[listener]);
+ });
+ self.global_events["movie.searcher.ended"] = function(notification) {
+ if (notification.data && self.data._id == notification.data._id) self.busy(false);
+ };
+ App.on("movie.searcher.ended", self.global_events["movie.searcher.ended"]);
+ self.global_events["release.update_status"] = function(notification) {
+ var data = notification.data;
+ if (data && self.data._id == data.movie_id) {
+ if (!self.data.releases) self.data.releases = [];
+ self.data.releases.push({
+ quality: data.quality,
+ status: data.status
+ });
+ self.updateReleases();
+ }
+ };
+ App.on("release.update_status", self.global_events["release.update_status"]);
+ },
+ destroy: function() {
+ var self = this;
+ self.el.destroy();
+ delete self.list.movies_added[self.get("id")];
+ self.list.movies.erase(self);
+ self.list.checkIfEmpty();
+ Object.each(self.global_events, function(handle, listener) {
+ App.off(listener, handle);
+ });
+ },
+ busy: function(set_busy, timeout) {
+ var self = this;
+ if (!set_busy) {
+ setTimeout(function() {
+ if (self.spinner) {
+ self.mask.fade("out");
+ setTimeout(function() {
+ if (self.mask) self.mask.destroy();
+ if (self.spinner) self.spinner.el.destroy();
+ self.spinner = null;
+ self.mask = null;
+ }, timeout || 400);
+ }
+ }, timeout || 1e3);
+ } else if (!self.spinner) {
+ self.createMask();
+ self.spinner = createSpinner(self.mask);
+ self.mask.fade("in");
+ }
+ },
+ createMask: function() {
+ var self = this;
+ self.mask = new Element("div.mask", {
+ styles: {
+ "z-index": 4
+ }
+ }).inject(self.el, "top").fade("hide");
+ },
+ update: function(notification) {
+ var self = this;
+ self.data = notification.data;
+ self.el.empty();
+ self.removeView();
+ self.profile = Quality.getProfile(self.data.profile_id) || {};
+ self.category = CategoryList.getCategory(self.data.category_id) || {};
+ self.create();
+ self.busy(false);
+ },
+ create: function() {
+ var self = this;
+ self.el.addClass("status_" + self.get("status"));
+ var eta = null, eta_date = null, now = Math.round(+new Date() / 1e3);
+ if (self.data.info.release_date) [ self.data.info.release_date.dvd, self.data.info.release_date.theater ].each(function(timestamp) {
+ if (timestamp > 0 && (eta == null || Math.abs(timestamp - now) < Math.abs(eta - now))) eta = timestamp;
+ });
+ if (eta) {
+ eta_date = new Date(eta * 1e3);
+ eta_date = eta_date.toLocaleString("en-us", {
+ month: "long"
+ }) + " " + eta_date.getFullYear();
+ }
+ self.el.adopt(self.select_checkbox = new Element("input[type=checkbox].inlay", {
+ events: {
+ change: function() {
+ self.fireEvent("select");
+ }
+ }
+ }), self.thumbnail = self.data.files && self.data.files.image_poster ? new Element("img", {
+ class: "type_image poster",
+ src: Api.createUrl("file.cache") + self.data.files.image_poster[0].split(Api.getOption("path_sep")).pop()
+ }) : null, self.data_container = new Element("div.data.inlay.light").adopt(self.info_container = new Element("div.info").adopt(new Element("div.title").adopt(self.title = new Element("a", {
+ events: {
+ click: function(e) {
+ self.episodes.open();
+ }
+ },
+ text: self.getTitle() || "n/a"
+ }), self.year = new Element("div.year", {
+ text: self.data.info.year || "n/a"
+ })), self.description = new Element("div.description.tiny_scroll", {
+ text: self.data.info.plot
+ }), self.eta = eta_date && now + 8035200 > eta ? new Element("div.eta", {
+ text: eta_date,
+ title: "ETA"
+ }) : null, self.quality = new Element("div.quality", {
+ events: {
+ click: function(e) {
+ var releases = self.el.getElement(".actions .releases");
+ if (releases.isVisible()) releases.fireEvent("click", [ e ]);
+ }
+ }
+ })), self.actions = new Element("div.actions")));
+ if (!self.thumbnail) self.el.addClass("no_thumbnail");
+ self.select_checkbox_class = new Form.Check(self.select_checkbox);
+ if (self.profile.data) self.profile.getTypes().each(function(type) {
+ var q = self.addQuality(type.get("quality"), type.get("3d"));
+ if ((type.finish == true || type.get("finish")) && !q.hasClass("finish")) {
+ q.addClass("finish");
+ q.set("title", q.get("title") + " Will finish searching for this movie if this quality is found.");
+ }
+ });
+ self.updateReleases();
+ Object.each(self.options.actions, function(action, key) {
+ self.action[key.toLowerCase()] = action = new self.options.actions[key](self);
+ if (action.el) self.actions.adopt(action);
+ });
+ },
+ updateReleases: function() {
+ var self = this;
+ if (!self.data.releases || self.data.releases.length == 0) return;
+ self.data.releases.each(function(release) {
+ var q = self.quality.getElement(".q_" + release.quality + (release.is_3d ? ".is_3d" : ":not(.is_3d)")), status = release.status;
+ if (!q && (status == "snatched" || status == "seeding" || status == "done")) q = self.addQuality(release.quality, release.is_3d || false);
+ if (q && !q.hasClass(status)) {
+ q.addClass(status);
+ q.set("title", (q.get("title") ? q.get("title") : "") + " status: " + status);
+ }
+ });
+ },
+ addQuality: function(quality, is_3d) {
+ var self = this;
+ var q = Quality.getQuality(quality);
+ return new Element("span", {
+ text: q.label + (is_3d ? " 3D" : ""),
+ class: "q_" + q.identifier + (is_3d ? " is_3d" : ""),
+ title: ""
+ }).inject(self.quality);
+ },
+ getTitle: function() {
+ var self = this;
+ if (self.data.title) return self.getUnprefixedTitle(self.data.title); else if (self.data.info.titles.length > 0) return self.getUnprefixedTitle(self.data.info.titles[0]);
+ return "Unknown movie";
+ },
+ getUnprefixedTitle: function(t) {
+ if (t.substr(0, 4).toLowerCase() == "the ") t = t.substr(4) + ", The"; else if (t.substr(0, 3).toLowerCase() == "an ") t = t.substr(3) + ", An"; else if (t.substr(0, 2).toLowerCase() == "a ") t = t.substr(2) + ", A";
+ return t;
+ },
+ slide: function(direction, el, expand) {
+ var self = this;
+ if (direction == "in") {
+ self.temp_view = self.view;
+ self.changeView("details");
+ self.el.addEvent("outerClick", function() {
+ self.removeView();
+ self.slide("out");
+ });
+ el.show();
+ if (expand === true) {
+ self.el.addClass("expanded");
+ self.el.getElements(".table").addClass("expanded");
+ }
+ self.data_container.addClass("hide_right");
+ } else {
+ self.el.removeEvents("outerClick");
+ setTimeout(function() {
+ if (self.el) {
+ self.el.getElements("> :not(.data):not(.poster):not(.movie_container)").hide();
+ self.el.getElements(".table").removeClass("expanded");
+ }
+ }, 600);
+ self.el.removeClass("expanded");
+ self.data_container.removeClass("hide_right");
+ }
+ },
+ changeView: function(new_view) {
+ var self = this;
+ if (self.el) self.el.removeClass(self.view + "_view").addClass(new_view + "_view");
+ self.view = new_view;
+ },
+ removeView: function() {
+ var self = this;
+ self.el.removeClass(self.view + "_view");
+ },
+ getIdentifier: function() {
+ var self = this;
+ try {
+ return self.get("identifiers").imdb;
+ } catch (e) {}
+ return self.get("imdb");
+ },
+ get: function(attr) {
+ return this.data[attr] || this.data.info[attr];
+ },
+ select: function(bool) {
+ var self = this;
+ self.select_checkbox_class[bool ? "check" : "uncheck"]();
+ },
+ isSelected: function() {
+ return this.select_checkbox.get("checked");
+ },
+ toElement: function() {
+ return this.el;
+ }
+});
+
+var NotificationBase = new Class({
+ Extends: BlockBase,
+ Implements: [ Options, Events ],
+ initialize: function(options) {
+ var self = this;
+ self.setOptions(options);
+ App.addEvent("unload", self.stopPoll.bind(self));
+ App.addEvent("reload", self.startInterval.bind(self, [ true ]));
+ App.on("notification", self.notify.bind(self));
+ App.on("message", self.showMessage.bind(self));
+ App.addEvent("loadSettings", self.addTestButtons.bind(self));
+ self.notifications = [];
+ App.addEvent("load", function() {
+ App.block.notification = new BlockMenu(self, {
+ button_class: "icon-notifications",
+ class: "notification_menu",
+ onOpen: self.markAsRead.bind(self)
+ });
+ $(App.block.notification).inject(App.getBlock("search"), "after");
+ self.badge = new Element("div.badge").inject(App.block.notification, "top").hide();
+ });
+ window.addEvent("load", function() {
+ self.startInterval.delay($(window).getSize().x <= 480 ? 2e3 : 100, self);
+ });
+ },
+ notify: function(result) {
+ var self = this;
+ var added = new Date();
+ added.setTime(result.added * 1e3);
+ result.el = App.getBlock("notification").addLink(new Element("span." + (result.read ? "read" : "")).adopt(new Element("span.message", {
+ html: result.message
+ }), new Element("span.added", {
+ text: added.timeDiffInWords(),
+ title: added
+ })), "top");
+ self.notifications.include(result);
+ if ((result.important !== undefined || result.sticky !== undefined) && !result.read) {
+ var sticky = true;
+ App.trigger("message", [ result.message, sticky, result ]);
+ } else if (!result.read) {
+ self.setBadge(self.notifications.filter(function(n) {
+ return !n.read;
+ }).length);
+ }
+ },
+ setBadge: function(value) {
+ var self = this;
+ self.badge.set("text", value);
+ self.badge[value ? "show" : "hide"]();
+ },
+ markAsRead: function(force_ids) {
+ var self = this, ids = force_ids;
+ if (!force_ids) {
+ var rn = self.notifications.filter(function(n) {
+ return !n.read && n.important === undefined;
+ });
+ ids = [];
+ rn.each(function(n) {
+ ids.include(n._id);
+ });
+ }
+ if (ids.length > 0) Api.request("notification.markread", {
+ data: {
+ ids: ids.join(",")
+ },
+ onSuccess: function() {
+ self.setBadge("");
+ }
+ });
+ },
+ startInterval: function(force) {
+ var self = this;
+ if (self.stopped && !force) {
+ self.stopped = false;
+ return;
+ }
+ self.request = Api.request("notification.listener", {
+ data: {
+ init: true
},
onSuccess: function(json) {
self.processData(json, true);
@@ -3415,185 +5061,6 @@ Profile.Type = new Class({
}
});
-var QualityBase = new Class({
- tab: "",
- content: "",
- setup: function(data) {
- var self = this;
- self.qualities = data.qualities;
- self.profiles_list = null;
- self.profiles = [];
- Array.each(data.profiles, self.createProfilesClass.bind(self));
- App.addEvent("loadSettings", self.addSettings.bind(self));
- },
- getProfile: function(id) {
- return this.profiles.filter(function(profile) {
- return profile.data._id == id;
- }).pick();
- },
- getActiveProfiles: function() {
- return Array.filter(this.profiles, function(profile) {
- return !profile.data.hide;
- });
- },
- getQuality: function(identifier) {
- try {
- return this.qualities.filter(function(q) {
- return q.identifier == identifier;
- }).pick();
- } catch (e) {}
- return {};
- },
- addSettings: function() {
- var self = this;
- self.settings = App.getPage("Settings");
- self.settings.addEvent("create", function() {
- var tab = self.settings.createSubTab("profile", {
- label: "Quality",
- name: "profile",
- subtab_label: "Qualities"
- }, self.settings.tabs.searcher, "searcher");
- self.tab = tab.tab;
- self.content = tab.content;
- self.createProfiles();
- self.createProfileOrdering();
- self.createSizes();
- });
- },
- createProfiles: function() {
- var self = this;
- var non_core_profiles = Array.filter(self.profiles, function(profile) {
- return !profile.isCore();
- });
- var count = non_core_profiles.length;
- self.settings.createGroup({
- label: "Quality Profiles",
- description: "Create your own profiles with multiple qualities."
- }).inject(self.content).adopt(self.profile_container = new Element("div.container"), new Element("a.add_new_profile", {
- text: count > 0 ? "Create another quality profile" : "Click here to create a quality profile.",
- events: {
- click: function() {
- var profile = self.createProfilesClass();
- $(profile).inject(self.profile_container);
- }
- }
- }));
- Array.each(non_core_profiles, function(profile) {
- $(profile).inject(self.profile_container);
- });
- },
- createProfilesClass: function(data) {
- var self = this;
- data = data || {
- id: randomString()
- };
- var profile = new Profile(data);
- self.profiles.include(profile);
- return profile;
- },
- createProfileOrdering: function() {
- var self = this;
- self.settings.createGroup({
- label: "Profile Defaults",
- description: "(Needs refresh '" + (App.isMac() ? "CMD+R" : "F5") + "' after editing)"
- }).grab(new Element(".ctrlHolder#profile_ordering").adopt(new Element("label[text=Order]"), self.profiles_list = new Element("ul"), new Element("p.formHint", {
- html: "Change the order the profiles are in the dropdown list. Uncheck to hide it completely.
First one will be default."
- }))).inject(self.content);
- Array.each(self.profiles, function(profile) {
- var check;
- new Element("li", {
- "data-id": profile.data._id
- }).adopt(check = new Element("input[type=checkbox]", {
- checked: !profile.data.hide,
- events: {
- change: self.saveProfileOrdering.bind(self)
- }
- }), new Element("span.profile_label", {
- text: profile.data.label
- }), new Element("span.handle.icon-handle")).inject(self.profiles_list);
- });
- var sorted_changed = false;
- self.profile_sortable = new Sortables(self.profiles_list, {
- revert: true,
- handle: ".handle",
- opacity: .5,
- onSort: function() {
- sorted_changed = true;
- },
- onComplete: function() {
- if (sorted_changed) {
- self.saveProfileOrdering();
- sorted_changed = false;
- }
- }
- });
- },
- saveProfileOrdering: function() {
- var self = this, ids = [], hidden = [];
- self.profiles_list.getElements("li").each(function(el, nr) {
- ids.include(el.get("data-id"));
- hidden[nr] = +!el.getElement("input[type=checkbox]").get("checked");
- });
- Api.request("profile.save_order", {
- data: {
- ids: ids,
- hidden: hidden
- }
- });
- },
- createSizes: function() {
- var self = this;
- var group = self.settings.createGroup({
- label: "Sizes",
- description: "Edit the minimal and maximum sizes (in MB) for each quality.",
- advanced: true,
- name: "sizes"
- }).inject(self.content);
- new Element("div.item.head.ctrlHolder").adopt(new Element("span.label", {
- text: "Quality"
- }), new Element("span.min", {
- text: "Min"
- }), new Element("span.max", {
- text: "Max"
- })).inject(group);
- Array.each(self.qualities, function(quality) {
- new Element("div.ctrlHolder.item").adopt(new Element("span.label", {
- text: quality.label
- }), new Element("input.min[type=text]", {
- value: quality.size_min,
- events: {
- keyup: function(e) {
- self.changeSize(quality.identifier, "size_min", e.target.get("value"));
- }
- }
- }), new Element("input.max[type=text]", {
- value: quality.size_max,
- events: {
- keyup: function(e) {
- self.changeSize(quality.identifier, "size_max", e.target.get("value"));
- }
- }
- })).inject(group);
- });
- },
- size_timer: {},
- changeSize: function(identifier, type, value) {
- var self = this;
- if (self.size_timer[identifier + type]) clearTimeout(self.size_timer[identifier + type]);
- self.size_timer[identifier + type] = function() {
- Api.request("quality.size.save", {
- data: {
- identifier: identifier,
- value_type: type,
- value: value
- }
- });
- }.delay(300);
- }
-});
-
-window.Quality = new QualityBase();
-
Page.Userscript = new Class({
Extends: PageBase,
order: 80,