var DownloadersBase = new Class({
Implements: [ Events ],
initialize: function() {
var self = this;
App.addEvent("loadSettings", self.addTestButtons.bind(self));
},
addTestButtons: function() {
var self = this;
var setting_page = App.getPage("Settings");
setting_page.addEvent("create", function() {
Object.each(setting_page.tabs.downloaders.groups, self.addTestButton.bind(self));
});
},
addTestButton: function(fieldset, plugin_name) {
var self = this, button_name = self.testButtonName(fieldset);
if (button_name.contains("Downloaders")) return;
new Element(".ctrlHolder.test_button").grab(new Element("a.button", {
text: button_name,
events: {
click: function() {
var button = fieldset.getElement(".test_button .button");
button.set("text", "Connecting...");
Api.request("download." + plugin_name + ".test", {
onComplete: function(json) {
button.set("text", button_name);
var message;
if (json.success) {
message = new Element("span.success", {
text: "Connection successful"
}).inject(button, "after");
} else {
var msg_text = "Connection failed. Check logs for details.";
if (json.hasOwnProperty("msg")) msg_text = json.msg;
message = new Element("span.failed", {
text: msg_text
}).inject(button, "after");
}
(function() {
message.destroy();
}).delay(3e3);
}
});
}
}
})).inject(fieldset);
},
testButtonName: function(fieldset) {
var name = fieldset.getElement("h2 .group_label").get("text");
return "Test " + name;
}
});
var Downloaders = new DownloadersBase();
var UpdaterBase = new Class({
Implements: [ Events ],
initialize: function() {
var self = this;
App.addEvent("load", self.info.bind(self, 2e3));
App.addEvent("unload", function() {
if (self.timer) clearTimeout(self.timer);
});
},
check: function(onComplete) {
var self = this;
Api.request("updater.check", {
onComplete: function(json) {
if (onComplete) onComplete(json);
if (json.update_available) self.doUpdate(); else {
App.unBlockPage();
App.trigger("message", [ "No updates available" ]);
}
}
});
},
info: function(timeout) {
var self = this;
if (self.timer) clearTimeout(self.timer);
self.timer = setTimeout(function() {
Api.request("updater.info", {
onComplete: function(json) {
self.json = json;
self.fireEvent("loaded", [ json ]);
if (json.update_version) {
self.createMessage(json);
} else {
if (self.message) self.message.destroy();
}
}
});
}, timeout || 0);
},
getInfo: function() {
return this.json;
},
createMessage: function(data) {
var self = this;
if (self.message) return;
var changelog = "https://github.com/" + data.repo_name + "/compare/" + data.version.hash + "..." + data.branch;
if (data.update_version.changelog) changelog = data.update_version.changelog + "#" + data.version.hash + "..." + data.update_version.hash;
self.message = new Element("div.message.update").adopt(new Element("span", {
text: "A new version is available"
}), new Element("a", {
href: changelog,
text: "see what has changed",
target: "_blank"
}), new Element("span[text=or]"), new Element("a", {
text: "just update, gogogo!",
events: {
click: self.doUpdate.bind(self)
}
})).inject(document.body);
},
doUpdate: function() {
var self = this;
App.blockPage("Please wait while CouchPotato is being updated with more awesome stuff.", "Updating");
Api.request("updater.update", {
onComplete: function(json) {
if (json.success) self.updating(); else App.unBlockPage();
}
});
},
updating: function() {
App.checkAvailable.delay(500, App, [ 1e3, function() {
window.location.reload();
} ]);
if (self.message) self.message.destroy();
}
});
var Updater = new UpdaterBase();
var PutIODownloader = new Class({
initialize: function() {
var self = this;
App.addEvent("loadSettings", self.addRegisterButton.bind(self));
},
addRegisterButton: function() {
var self = this;
var setting_page = App.getPage("Settings");
setting_page.addEvent("create", function() {
var fieldset = setting_page.tabs.downloaders.groups.putio, l = window.location;
var putio_set = 0;
fieldset.getElements("input[type=text]").each(function(el) {
putio_set += +(el.get("value") !== "");
});
new Element(".ctrlHolder").adopt(putio_set > 0 ? [ self.unregister = new Element("a.button.red", {
text: 'Unregister "' + fieldset.getElement("input[name*=oauth_token]").get("value") + '"',
events: {
click: function() {
fieldset.getElements("input[name*=oauth_token]").set("value", "").fireEvent("change");
self.unregister.destroy();
self.unregister_or.destroy();
}
}
}), self.unregister_or = new Element("span[text=or]") ] : null, new Element("a.button", {
text: putio_set > 0 ? "Register a different account" : "Register your put.io account",
events: {
click: function() {
Api.request("downloader.putio.auth_url", {
data: {
host: l.protocol + "//" + l.hostname + (l.port ? ":" + l.port : "")
},
onComplete: function(json) {
window.location = json.url;
}
});
}
}
})).inject(fieldset.getElement(".test_button"), "before");
});
}
});
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: {},
create: function() {
var self = this;
var focus_timer = 0;
self.el = new Element("div.search_form").adopt(new Element("a.icon-search", {
events: {
click: self.clear.bind(self)
}
}), self.wrapper = new Element("div.wrapper").adopt(self.result_container = new Element("div.results_container", {
events: {
mousewheel: function(e) {
e.stopPropagation();
}
}
}).grab(self.results = new Element("div.results")), new Element("div.input").grab(self.input = new Element("input", {
placeholder: "Search & add a new media",
events: {
input: self.keyup.bind(self),
paste: self.keyup.bind(self),
change: self.keyup.bind(self),
keyup: self.keyup.bind(self),
focus: function() {
if (focus_timer) clearTimeout(focus_timer);
if (this.get("value")) self.hideResults(false);
},
blur: function() {
focus_timer = function() {
self.el.removeClass("focused");
}.delay(100);
}
}
}))));
self.mask = new Element("div.mask").inject(self.result_container);
},
clear: function(e) {
var self = this;
e.preventDefault();
if (self.last_q === "") {
self.input.blur();
self.last_q = null;
} else {
self.last_q = "";
self.input.set("value", "");
self.el.addClass("focused");
self.input.focus();
self.media = {};
self.results.empty();
self.el.removeClass("filled");
dynamics.css(self.wrapper, {
opacity: 0,
scale: .1
});
dynamics.animate(self.wrapper, {
opacity: 1,
scale: 1
}, {
type: dynamics.spring,
frequency: 200,
friction: 270,
duration: 800
});
}
},
hideResults: function(bool) {
var self = this;
if (self.hidden == bool) return;
self.el[bool ? "removeClass" : "addClass"]("shown");
if (bool) {
History.removeEvent("change", self.hideResults.bind(self, !bool));
self.el.removeEvent("outerClick", self.hideResults.bind(self, !bool));
} else {
History.addEvent("change", self.hideResults.bind(self, !bool));
self.el.addEvent("outerClick", self.hideResults.bind(self, !bool));
}
self.hidden = bool;
},
keyup: function() {
var self = this;
self.el[self.q() ? "addClass" : "removeClass"]("filled");
if (self.q() != self.last_q) {
if (self.api_request && self.api_request.isRunning()) self.api_request.cancel();
if (self.autocomplete_timer) clearTimeout(self.autocomplete_timer);
self.autocomplete_timer = self.autocomplete.delay(300, self);
}
},
autocomplete: function() {
var self = this;
if (!self.q()) {
self.hideResults(true);
return;
}
self.list();
},
list: function() {
var self = this, q = self.q(), cache = self.cache[q];
self.hideResults(false);
if (!cache) {
setTimeout(function() {
self.mask.addClass("show");
}, 10);
if (!self.spinner) self.spinner = createSpinner(self.mask);
self.api_request = Api.request("search", {
data: {
q: q
},
onComplete: self.fill.bind(self, q)
});
} else self.fill(q, cache);
self.last_q = q;
},
fill: function(q, json) {
var self = this;
self.cache[q] = json;
self.media = {};
self.results.empty();
Object.each(json, function(media) {
if (typeOf(media) == "array") {
Object.each(media, function(me) {
var m = new (window["BlockSearch" + me.type.capitalize() + "Item"])(me);
$(m).inject(self.results);
self.media[m.imdb || "r-" + Math.floor(Math.random() * 1e4)] = m;
if (q == m.imdb) m.showOptions();
});
}
});
self.mask.removeClass("show");
},
loading: function(bool) {
this.el[bool ? "addClass" : "removeClass"]("loading");
},
q: function() {
return this.input.get("value").trim();
}
});
var MovieDetails = new Class({
Extends: BlockBase,
sections: null,
buttons: null,
initialize: function(parent, options) {
var self = this;
self.sections = {};
var category = parent.get("category"), profile = parent.profile;
self.el = new Element("div", {
class: "page active movie_details level_" + (options.level || 0)
}).adopt(self.overlay = new Element("div.overlay", {
events: {
click: self.close.bind(self)
}
}).grab(new Element("a.close.icon-left-arrow")), self.content = new Element("div.scroll_content").grab(new Element("div.head").adopt(new Element("h1").grab(self.title_dropdown = new BlockMenu(self, {
class: "title",
button_text: parent.getTitle() + (parent.get("year") ? " (" + parent.get("year") + ")" : ""),
button_class: "icon-dropdown"
})), self.buttons = new Element("div.buttons"))));
self.addSection("description", new Element("div", {
text: parent.get("plot")
}));
var titles = parent.get("info").titles;
$(self.title_dropdown).addEvents({
"click:relay(li a)": function(e, el) {
e.stopPropagation();
Api.request("movie.edit", {
data: {
id: parent.get("_id"),
default_title: el.get("text")
}
});
$(self.title_dropdown).getElements(".icon-ok").removeClass("icon-ok");
el.addClass("icon-ok");
self.title_dropdown.button.set("text", el.get("text") + (parent.get("year") ? " (" + parent.get("year") + ")" : ""));
}
});
titles.each(function(t) {
self.title_dropdown.addLink(new Element("a", {
text: t,
class: parent.get("title") == t ? "icon-ok" : ""
}));
});
},
addSection: function(name, section_el) {
var self = this;
name = name.toLowerCase();
self.content.grab(self.sections[name] = new Element("div", {
class: "section section_" + name
}).grab(section_el));
},
addButton: function(button) {
var self = this;
self.buttons.grab(button);
},
open: function() {
var self = this;
self.el.addClass("show");
if (!App.mobile_screen) {
$(self.content).getElements("> .head, > .section").each(function(section, nr) {
dynamics.css(section, {
opacity: 0,
translateY: 100
});
dynamics.animate(section, {
opacity: 1,
translateY: 0
}, {
type: dynamics.spring,
frequency: 200,
friction: 300,
duration: 1200,
delay: 500 + nr * 100
});
});
}
},
close: function() {
var self = this;
var ended = function() {
self.el.dispose();
self.overlay.removeEventListener("transitionend", ended);
};
self.overlay.addEventListener("transitionend", ended, false);
if (!App.mobile_screen) {
$(self.content).getElements("> .head, > .section").reverse().each(function(section, nr) {
dynamics.animate(section, {
opacity: 0,
translateY: 100
}, {
type: dynamics.spring,
frequency: 200,
friction: 300,
duration: 1200,
delay: nr * 50
});
});
dynamics.setTimeout(function() {
self.el.removeClass("show");
}, 200);
} else {
self.el.removeClass("show");
}
}
});
var MovieList = new Class({
Implements: [ Events, Options ],
options: {
api_call: "media.list",
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.movies").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", {
events: {
"click:relay(.movie)": function(e, el) {
el.retrieve("klass").onClick(e);
},
"mouseenter:relay(.movie)": function(e, el) {
e.stopPropagation();
el.retrieve("klass").onMouseenter(e);
},
"mouseleave:relay(.movie)": function(e, el) {
e.stopPropagation();
el.retrieve("klass").onMouseleave(e);
},
"change:relay(.movie input)": function(e, el) {
e.stopPropagation();
el = el.getParent();
var klass = el.retrieve("klass");
klass.fireEvent("select");
klass.select(klass.select_checkbox.get("checked"));
}
}
}), self.load_more = self.options.load_more ? new Element("a.load_more", {
events: {
click: self.loadMore.bind(self)
}
}) : null);
self.changeView(self.getSavedView() || self.options.view || "thumb");
if (self.options.navigation) self.createNavigation();
if (self.options.api_call) 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.createMovie(notification.data, "top");
self.setCounter(self.counter_count + 1);
self.checkIfEmpty();
}
},
create: function() {
var self = this;
if (self.options.load_more) {
self.scrollspy = new ScrollSpy({
container: self.el.getParent(),
min: function() {
return self.load_more.getCoordinates().top;
},
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();
}
self.createMovie(movies, "bottom");
self.total_movies += total;
self.setCounter(total);
self.calculateSelected();
},
setCounter: function(count) {
var self = this;
if (!self.navigation_counter) return;
self.counter_count = count;
self.navigation_counter.set("text", (count || 0) + " movies");
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 movies 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);
}
},
createMovie: function(movie, inject_at, nr) {
var self = this, movies = Array.isArray(movie) ? movie : [ movie ], movie_els = [];
inject_at = inject_at || "bottom";
movies.each(function(movie, nr) {
var m = new Movie(self, {
actions: self.options.actions,
view: self.current_view,
onSelect: self.calculateSelected.bind(self)
}, movie);
var el = $(m);
if (inject_at === "bottom") {
movie_els.push(el);
} else {
el.inject(self.movie_list, inject_at);
}
self.movies.include(m);
self.movies_added[movie._id] = true;
});
if (movie_els.length > 0) {
$(self.movie_list).adopt(movie_els);
}
},
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]", {
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 BlockMenu(self, {
class: "filter",
button_class: "icon-filter"
}), self.navigation_actions = new Element("div.actions", {
events: {
click: function(e, el) {
e.preventDefault();
var new_view = self.current_view == "list" ? "thumb" : "list";
var a = "active";
self.navigation_actions.getElements("." + a).removeClass(a);
self.changeView(new_view);
self.navigation_actions.getElement("[data-view=" + new_view + "]").addClass(a);
}
}
}), self.navigation_menu = new BlockMenu(self, {
class: "extra",
button_class: "icon-dots"
})));
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 icon-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({
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);
}
}
}));
[ "thumb", "list" ].each(function(view) {
var current = self.current_view == view;
new Element("a", {
class: "icon-" + 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;
document.body[selected > 0 ? "addClass" : "removeClass"]("mass_editing");
if (self.mass_edit_select) {
self.mass_edit_select.set("checked", checked);
self.mass_edit_select.indeterminate = 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 references in CouchPotato";
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 + "_view", new_view, {
duration: 1e3
});
},
getSavedView: function() {
var self = this;
return self.options.force_view ? self.options.view : Cookie.read(self.options.identifier + "_view");
},
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...");
}
var loader_timeout;
if (self.movies.length === 0 && self.options.loader) {
self.loader_first = new Element("div.mask.loading.with_message").grab(new Element("div.message", {
text: self.options.title ? "Loading '" + self.options.title + "'" : "Loading..."
})).inject(self.el, "top");
createSpinner(self.loader_first);
var lfc = self.loader_first;
loader_timeout = setTimeout(function() {
lfc.addClass("show");
}, 10);
self.el.setStyle("min-height", 220);
}
Api.request(self.options.api_call, {
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 (loader_timeout) clearTimeout(loader_timeout);
if (self.loader_first) {
var lf = self.loader_first;
self.loader_first = null;
lf.removeClass("show");
setTimeout(function() {
lf.destroy();
}, 1e3);
self.el.setStyle("min-height", null);
}
self.store(json.movies);
self.addMovies(json.movies, json.total || json.movies.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;
}
});
var MoviesManage = new Class({
Extends: PageBase,
order: 20,
name: "manage",
title: "Do stuff to your existing movies!",
indexAction: function() {
var self = this;
if (!self.list) {
self.refresh_button = new Element("a", {
title: "Rescan your library for new movies",
text: "Full library refresh",
events: {
click: self.refresh.bind(self, true)
}
});
self.refresh_quick = new Element("a", {
title: "Just scan for recently changed",
text: "Quick library scan",
events: {
click: self.refresh.bind(self, false)
}
});
self.list = new MovieList({
identifier: "manage",
filter: {
status: "done",
release_status: "done",
status_or: 1
},
actions: [ MA.IMDB, MA.Files, MA.Trailer, MA.Readd, MA.Delete ],
menu: [ self.refresh_button, self.refresh_quick ],
on_empty_element: new Element("div.empty_manage").adopt(new Element("div", {
text: "Seems like you don't have anything in your library yet. Add your existing movie folders in "
}).grab(new Element("a", {
text: "Settings > Manage",
href: App.createUrl("settings/manage")
})), new Element("div.after_manage", {
text: "When you've done that, hit this button → "
}).grab(new Element("a.button.green", {
text: "Hit me, but not too hard",
events: {
click: self.refresh.bind(self, true)
}
})))
});
$(self.list).inject(self.content);
self.startProgressInterval();
}
},
refresh: function(full) {
var self = this;
if (!self.update_in_progress) {
Api.request("manage.update", {
data: {
full: +full
}
});
self.startProgressInterval();
}
},
startProgressInterval: function() {
var self = this;
self.progress_interval = setInterval(function() {
if (self.progress_request && self.progress_request.running) return;
self.update_in_progress = true;
self.progress_request = Api.request("manage.progress", {
onComplete: function(json) {
if (!json || !json.progress) {
clearInterval(self.progress_interval);
self.update_in_progress = false;
if (self.progress_container) {
self.progress_container.destroy();
self.list.update();
}
} else {
var progress = json.progress;
if (!self.list.navigation) return;
if (!self.progress_container) self.progress_container = new Element("div.progress").inject(self.list.navigation, "after");
self.progress_container.empty();
var sorted_table = self.parseProgress(json.progress);
sorted_table.each(function(folder) {
var folder_progress = progress[folder];
new Element("div").adopt(new Element("span.folder", {
text: folder + (folder_progress.eta > 0 ? ", " + new Date().increment("second", folder_progress.eta).timeDiffInWords().replace("from now", "to go") : "")
}), new Element("span.percentage", {
text: folder_progress.total ? Math.round((folder_progress.total - folder_progress.to_go) / folder_progress.total * 100) + "%" : "0%"
})).inject(self.progress_container);
});
}
}
});
}, 1e3);
},
parseProgress: function(progress_object) {
var folder, temp_array = [];
for (folder in progress_object) {
if (progress_object.hasOwnProperty(folder)) {
temp_array.push(folder);
}
}
return temp_array.stableSort();
}
});
var MovieAction = new Class({
Implements: [ Options ],
class_name: "action",
label: "UNKNOWN",
icon: null,
button: null,
details: null,
detail_button: null,
initialize: function(movie, options) {
var self = this;
self.setOptions(options);
self.movie = movie;
self.create();
if (self.button) {
var wrapper = new Element("div", {
class: self.class_name
});
self.button.inject(wrapper);
self.button = wrapper;
}
},
create: function() {},
getButton: function() {
return this.button || null;
},
getDetails: function() {
return this.details || null;
},
getDetailButton: function() {
return this.detail_button || null;
},
getLabel: function() {
return this.label;
},
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.movie.getTitle(true);
} catch (e) {
try {
return self.movie.original_title ? self.movie.original_title : self.movie.titles[0];
} catch (e2) {
return "Unknown";
}
}
},
get: function(key) {
var self = this;
try {
return self.movie.get(key);
} catch (e) {
return self.movie[key];
}
},
createMask: function() {
var self = this;
self.mask = new Element("div.mask", {
styles: {
"z-index": "1"
}
}).inject(self.movie, "top").fade("hide");
},
toElement: function() {
return this.el || null;
}
});
var MA = {};
MA.IMDB = new Class({
Extends: MovieAction,
id: null,
create: function() {
var self = this;
self.id = self.movie.getIdentifier ? self.movie.getIdentifier() : self.get("imdb");
self.button = self.createButton();
self.detail_button = self.createButton();
if (!self.id) self.disable();
},
createButton: function() {
var self = this;
return new Element("a.imdb", {
text: "IMDB",
title: "Go to the IMDB page of " + self.getTitle(),
href: "http://www.imdb.com/title/" + self.id + "/",
target: "_blank"
});
}
});
MA.Release = new Class({
Extends: MovieAction,
label: "Releases",
create: function() {
var self = this;
App.on("movie.searcher.ended", function(notification) {
if (self.movie.data._id != notification.data._id) return;
self.releases = null;
if (self.options_container) {
if (self.options_container.isDisplayed()) {
self.options_container.destroy();
self.getDetails();
} else {
self.options_container.destroy();
self.options_container = null;
}
}
});
},
getDetails: function(refresh) {
var self = this;
if (!self.movie.data.releases || self.movie.data.releases.length === 0) return;
if (!self.options_container || refresh) {
self.options_container = new Element("div.options").grab(self.release_container = 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"
}), new Element("span.actions")).inject(self.release_container);
if (self.movie.data.releases) self.movie.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) {}
}
var size = info.size ? Math.floor(self.get(release, "size")) : 0;
size = size ? size < 1e3 ? size + "MB" : Math.round(size * 10 / 1024) / 10 + "GB" : "n/a";
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: size
}), 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
}), new Element("span.actions").adopt(info.detail_url ? new Element("a.icon-info", {
href: info.detail_url,
target: "_blank"
}) : new Element("a"), new Element("a.icon-download", {
events: {
click: function(e) {
e.stopPropagation();
if (!this.hasClass("completed")) self.download(release);
}
}
}), new Element("a", {
class: release.status == "ignored" ? "icon-redo" : "icon-cancel",
events: {
click: function(e) {
e.stopPropagation();
self.ignore(release);
this.toggleClass("icon-redo");
this.toggleClass("icon-cancel");
}
}
}))).inject(self.release_container);
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.movie.quality.getElement(".q_" + release.quality), new_status = notification.data.status;
release.el.set("class", "item " + new_status);
var status_el = release.el.getElement(".status");
status_el.set("class", "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.release_container.getElements("#release_" + self.last_release._id).addClass("last_release");
if (self.next_release) self.release_container.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.release_container, "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.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.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;
}
return self.options_container;
},
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.release_container.getElement("#release_" + release._id), icon = release_el.getElement(".icon-download");
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
}
});
}
});
MA.Trailer = new Class({
Extends: MovieAction,
id: null,
label: "Trailer",
getDetails: function() {
var self = this, data_url = 'https://www.googleapis.com/youtube/v3/search?q="{title}" {year} trailer&maxResults=1&type=video&videoDefinition=high&videoEmbeddable=true&part=snippet&key=AIzaSyAT3li1KjfLidaL6Vt8T92MRU7n4VOrjYk';
if (!self.player_container) {
self.id = "trailer-" + randomString();
self.player_container = new Element("div.icon-play[id=" + self.id + "]", {
events: {
click: self.watch.bind(self)
}
}).adopt(new Element('span[text="watch"]'), new Element('span[text="trailer"]'));
self.container = new Element("div.trailer_container").grab(self.player_container);
var url = data_url.substitute({
title: encodeURI(self.getTitle()),
year: self.get("year")
});
new Request.JSONP({
url: url,
onComplete: function(json) {
self.video_id = json.items[0].id.videoId;
self.container.grab(new Element("div.background", {
styles: {
"background-image": "url(" + json.items[0].snippet.thumbnails.high.url + ")"
}
}));
}
}).send();
}
return self.container;
},
watch: function() {
var self = this;
self.container.set("html", '');
}
});
MA.Category = new Class({
Extends: MovieAction,
create: function() {
var self = this;
var category = self.movie.get("category");
self.detail_button = new BlockMenu(self, {
class: "category",
button_text: category ? category.label : "No category",
button_class: "icon-dropdown"
});
var categories = CategoryList.getAll();
if (categories.length > 0) {
$(self.detail_button).addEvents({
"click:relay(li a)": function(e, el) {
e.stopPropagation();
Api.request("movie.edit", {
data: {
id: self.movie.get("_id"),
category_id: el.get("data-id")
}
});
$(self.detail_button).getElements(".icon-ok").removeClass("icon-ok");
el.addClass("icon-ok");
self.detail_button.button.set("text", el.get("text"));
}
});
self.detail_button.addLink(new Element("a[text=No category]", {
class: !category ? "icon-ok" : "",
"data-id": ""
}));
categories.each(function(c) {
self.detail_button.addLink(new Element("a", {
text: c.get("label"),
class: category && category._id == c.get("_id") ? "icon-ok" : "",
"data-id": c.get("_id")
}));
});
} else {
$(self.detail_button).hide();
}
}
});
MA.Profile = new Class({
Extends: MovieAction,
create: function() {
var self = this;
var profile = self.movie.profile;
self.detail_button = new BlockMenu(self, {
class: "profile",
button_text: profile ? profile.get("label") : "No profile",
button_class: "icon-dropdown"
});
var profiles = Quality.getActiveProfiles();
if (profiles.length > 0) {
$(self.detail_button).addEvents({
"click:relay(li a)": function(e, el) {
e.stopPropagation();
Api.request("movie.edit", {
data: {
id: self.movie.get("_id"),
profile_id: el.get("data-id")
}
});
$(self.detail_button).getElements(".icon-ok").removeClass("icon-ok");
el.addClass("icon-ok");
self.detail_button.button.set("text", el.get("text"));
}
});
profiles.each(function(pr) {
self.detail_button.addLink(new Element("a", {
text: pr.get("label"),
class: profile && profile.get("_id") == pr.get("_id") ? "icon-ok" : "",
"data-id": pr.get("_id")
}));
});
} else {
$(self.detail_button).hide();
}
}
});
MA.Refresh = new Class({
Extends: MovieAction,
icon: "refresh",
create: function() {
var self = this;
self.button = self.createButton();
self.detail_button = self.createButton();
},
createButton: function() {
var self = this;
return new Element("a.refresh", {
text: "Refresh",
title: "Refresh the movie info and do a forced search",
events: {
click: self.doRefresh.bind(self)
}
});
},
doRefresh: function(e) {
var self = this;
e.stop();
Api.request("media.refresh", {
data: {
id: self.movie.get("_id")
}
});
}
});
var SuggestBase = new Class({
Extends: MovieAction,
getIMDB: function() {
return this.movie.data.info.imdb;
},
refresh: function(json) {
var self = this;
if (json && json.movie) {
self.movie.list.addMovies([ json.movie ], 1);
var last_added = self.movie.list.movies[self.movie.list.movies.length - 1];
$(last_added).inject(self.movie, "before");
}
self.movie.destroy();
}
});
MA.Add = new Class({
Extends: SuggestBase,
label: "Add",
icon: "plus",
create: function() {
var self = this;
self.button = new Element("a.add", {
text: "Add",
title: "Re-add the movie and mark all previous snatched/downloaded as ignored",
events: {
click: function() {
self.movie.openDetails();
}
}
});
},
getDetails: function() {
var self = this;
var m = new BlockSearchMovieItem(self.movie.data.info, {
onAdded: self.movie.data.status == "suggested" ? function() {
Api.request("suggestion.ignore", {
data: {
imdb: self.movie.data.info.imdb,
remove_only: true
},
onComplete: self.refresh.bind(self)
});
} : function() {
self.movie.destroy();
}
});
m.showOptions();
return m;
}
});
MA.SuggestSeen = new Class({
Extends: SuggestBase,
icon: "eye",
create: function() {
var self = this;
self.button = self.createButton();
self.detail_button = self.createButton();
},
createButton: function() {
var self = this;
return new Element("a.seen", {
text: "Already seen",
title: "Already seen it!",
events: {
click: self.markAsSeen.bind(self)
}
});
},
markAsSeen: function(e) {
var self = this;
e.stopPropagation();
Api.request("suggestion.ignore", {
data: {
imdb: self.getIMDB(),
mark_seen: 1
},
onComplete: function(json) {
self.refresh(json);
if (self.movie.details) {
self.movie.details.close();
}
}
});
}
});
MA.SuggestIgnore = new Class({
Extends: SuggestBase,
icon: "error",
create: function() {
var self = this;
self.button = self.createButton();
self.detail_button = self.createButton();
},
createButton: function() {
var self = this;
return new Element("a.ignore", {
text: "Ignore",
title: "Don't suggest this movie anymore",
events: {
click: self.markAsIgnored.bind(self)
}
});
},
markAsIgnored: function(e) {
var self = this;
e.stopPropagation();
Api.request("suggestion.ignore", {
data: {
imdb: self.getIMDB()
},
onComplete: function(json) {
self.refresh(json);
if (self.movie.details) {
self.movie.details.close();
}
}
});
}
});
MA.ChartIgnore = new Class({
Extends: SuggestBase,
icon: "error",
create: function() {
var self = this;
self.button = self.createButton();
self.detail_button = self.createButton();
},
createButton: function() {
var self = this;
return new Element("a.ignore", {
text: "Hide",
title: "Don't show this movie in charts",
events: {
click: self.markAsHidden.bind(self)
}
});
},
markAsHidden: function(e) {
var self = this;
e.stopPropagation();
Api.request("charts.ignore", {
data: {
imdb: self.getIMDB()
},
onComplete: function(json) {
if (self.movie.details) {
self.movie.details.close();
}
self.movie.destroy();
}
});
}
});
MA.Readd = new Class({
Extends: MovieAction,
create: function() {
var self = this, movie_done = self.movie.data.status == "done", snatched;
if (self.movie.data.releases && !movie_done) snatched = self.movie.data.releases.filter(function(release) {
return release.status && (release.status == "snatched" || release.status == "seeding" || release.status == "downloaded" || release.status == "done");
}).length;
if (movie_done || snatched && snatched > 0) self.el = new Element("a.readd", {
title: "Re-add the movie and mark all previous snatched/downloaded as ignored",
events: {
click: self.doReadd.bind(self)
}
});
},
doReadd: function(e) {
var self = this;
e.stopPropagation();
Api.request("movie.add", {
data: {
identifier: self.movie.getIdentifier(),
ignore_previous: 1
}
});
}
});
MA.Delete = new Class({
Extends: MovieAction,
Implements: [ Chain ],
create: function() {
var self = this;
self.button = self.createButton();
self.detail_button = self.createButton();
},
createButton: function() {
var self = this;
return new Element("a.delete", {
text: "Delete",
title: "Remove the movie from this CP list",
events: {
click: self.showConfirm.bind(self)
}
});
},
showConfirm: function(e) {
var self = this;
e.stopPropagation();
self.question = new Question("Are you sure you want to delete " + self.getTitle() + "?", "", [ {
text: "Yes, delete " + self.getTitle(),
class: "delete",
events: {
click: function(e) {
e.target.set("text", "Deleting...");
self.del();
}
}
}, {
text: "Cancel",
cancel: true
} ]);
},
del: function() {
var self = this;
var movie = $(self.movie);
Api.request("media.delete", {
data: {
id: self.movie.get("_id"),
delete_from: self.movie.list.options.identifier
},
onComplete: function() {
if (self.question) self.question.close();
dynamics.animate(movie, {
opacity: 0,
scale: 0
}, {
type: dynamics.bezier,
points: [ {
x: 0,
y: 0,
cp: [ {
x: .876,
y: 0
} ]
}, {
x: 1,
y: 1,
cp: [ {
x: .145,
y: 1
} ]
} ],
duration: 400,
complete: function() {
self.movie.destroy();
}
});
}
});
}
});
MA.Files = new Class({
Extends: MovieAction,
label: "Files",
getDetails: function() {
var self = this;
if (!self.movie.data.releases || self.movie.data.releases.length === 0) return;
if (!self.files_container) {
self.files_container = new Element("div.files.table");
new Element("div.item.head").adopt(new Element("span.name", {
text: "File"
}), new Element("span.type", {
text: "Type"
})).inject(self.files_container);
if (self.movie.data.releases) Array.each(self.movie.data.releases, function(release) {
var rel = new Element("div.release").inject(self.files_container);
Object.each(release.files, function(files, type) {
Array.each(files, function(file) {
new Element("div.file.item").adopt(new Element("span.name", {
text: file
}), new Element("span.type", {
text: type
})).inject(rel);
});
});
});
}
return self.files_container;
}
});
MA.MarkAsDone = new Class({
Extends: MovieAction,
create: function() {
var self = this;
self.button = self.createButton();
self.detail_button = self.createButton();
},
createButton: function() {
var self = this;
return new Element("a.mark_as_done", {
text: "Mark as done",
title: "Remove from available list and move to managed movies",
events: {
click: self.markMovieDone.bind(self)
}
});
},
markMovieDone: function() {
var self = this;
Api.request("media.delete", {
data: {
id: self.movie.get("_id"),
delete_from: "wanted"
},
onComplete: function() {
self.movie.destroy();
}
});
}
});
var Movie = new Class({
Extends: BlockBase,
Implements: [ Options, Events ],
actions: null,
details: null,
initialize: function(list, options, data) {
var self = this;
self.actions = [];
self.data = data;
self.list = list;
self.buttons = [];
self.el = new Element("a.movie");
self.el.store("klass", self);
self.profile = Quality.getProfile(data.profile_id) || {};
self.category = CategoryList.getCategory(data.category_id) || {};
self.parent(self, options);
self.addEvents();
},
openDetails: function() {
var self = this;
if (!self.details) {
self.details = new MovieDetails(self, {
level: 3
});
self.actions.each(function(action, nr) {
var details = action.getDetails();
if (details) {
self.details.addSection(action.getLabel(), details);
} else {
var button = action.getDetailButton();
if (button) {
self.details.addButton(button);
}
}
});
}
App.getPageContainer().grab(self.details);
self.details.open.delay(10, self.details);
},
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.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.media_id) {
if (!self.data.releases) self.data.releases = [];
var updated = false;
self.data.releases.each(function(release) {
if (release._id == data._id) {
release.status = data.status;
updated = true;
}
});
if (updated) 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();
if (self.details) self.details.close();
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.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.actions = [];
self.data = notification.data;
self.el.empty();
self.profile = Quality.getProfile(self.data.profile_id) || {};
self.category = CategoryList.getCategory(self.data.category_id) || {};
self.create();
self.select(self.select_checkbox.get("checked"));
self.busy(false);
},
create: function() {
var self = this, d = new Date();
self.el.addClass("status_" + self.get("status"));
var eta = null, eta_date = null, now = Math.round(+d / 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);
if (+eta_date / 1e3 < now) {
eta_date = null;
} else {
eta_date = eta_date.format("%b") + (d.getFullYear() != eta_date.getFullYear() ? " " + eta_date.getFullYear() : "");
}
}
var rating, stars;
if ([ "suggested", "chart" ].indexOf(self.data.status) > -1 && self.data.info && self.data.info.rating && self.data.info.rating.imdb) {
rating = self.data.info.rating.imdb;
stars = [];
var half_rating = rating[0] / 2;
for (var i = 1; i <= 5; i++) {
if (half_rating >= 1) stars.push(new Element("span.icon-star")); else if (half_rating > 0) stars.push(new Element("span.icon-star-half")); else stars.push(new Element("span.icon-star-empty"));
half_rating -= 1;
}
}
var thumbnail = new Element("div.poster");
if (self.data.files && self.data.files.image_poster && self.data.files.image_poster.length > 0) {
thumbnail = new Element("div", {
class: "type_image poster",
styles: {
"background-image": "url(" + Api.createUrl("file.cache") + self.data.files.image_poster[0].split(Api.getOption("path_sep")).pop() + ")"
}
});
} else if (self.data.info && self.data.info.images && self.data.info.images.poster && self.data.info.images.poster.length > 0) {
thumbnail = new Element("div", {
class: "type_image poster",
styles: {
"background-image": "url(" + self.data.info.images.poster[0] + ")"
}
});
}
self.el.adopt(self.select_checkbox = new Element("input[type=checkbox]"), new Element("div.poster_container").adopt(self.thumbnail = thumbnail, self.actions_el = new Element("div.actions")), self.data_container = new Element("div.data.light").grab(self.info_container = new Element("div.info").adopt(new Element("div.title").adopt(self.title = new Element("span", {
text: self.getTitle() || "n/a"
}), self.year = new Element("div.year", {
text: self.data.info.year || "n/a"
})), self.eta = eta_date && now + 8035200 > eta ? new Element("div.eta", {
text: eta_date,
title: "ETA"
}) : null, self.quality = new Element("div.quality"), self.rating = rating ? new Element("div.rating[title=" + rating[0] + "]").adopt(stars, new Element("span.votes[text=(" + rating.join(" / ") + ")][title=Votes]")) : null)));
if (!self.thumbnail) self.el.addClass("no_thumbnail");
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();
},
onClick: function(e) {
var self = this;
if (e.target.getParents(".actions").length == 0 && e.target != self.select_checkbox) {
e.stopPropagation();
self.openDetails();
}
},
onMouseenter: function() {
var self = this;
if (self.actions.length <= 0) {
self.options.actions.each(function(a) {
var action = new a(self), button = action.getButton();
if (button) {
self.actions_el.grab(button);
self.buttons.push(button);
}
self.actions.push(action);
});
}
if (App.mobile_screen) return;
if (self.list.current_view == "thumb") {
dynamics.css(self.el, {
scale: 1,
opacity: 1
});
dynamics.animate(self.el, {
scale: .9
}, {
type: dynamics.bounce
});
self.buttons.each(function(el, nr) {
dynamics.css(el, {
opacity: 0,
translateY: 50
});
dynamics.animate(el, {
opacity: 1,
translateY: 0
}, {
type: dynamics.spring,
frequency: 200,
friction: 300,
duration: 800,
delay: 100 + nr * 40
});
});
}
},
onMouseleave: function() {
var self = this;
if (App.mobile_screen) return;
if (self.list.current_view == "thumb") {
dynamics.animate(self.el, {
scale: 1
}, {
type: dynamics.spring
});
}
},
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(prefixed) {
var self = this;
if (self.data.title) return prefixed ? self.data.title : self.getUnprefixedTitle(self.data.title); else if (self.data.info && self.data.info.titles && self.data.info.titles.length > 0) return prefixed ? self.data.info.titles[0] : 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;
},
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(select) {
var self = this;
self.select_checkbox.set("checked", select);
self.el[self.select_checkbox.get("checked") ? "addClass" : "removeClass"]("checked");
},
isSelected: function() {
return this.select_checkbox.get("checked");
},
toElement: function() {
return this.el;
}
});
Page.Movies = new Class({
Extends: PageBase,
name: "movies",
icon: "movie",
sub_pages: [ "Wanted", "Manage" ],
default_page: "Wanted",
current_page: null,
initialize: function(parent, options) {
var self = this;
self.parent(parent, options);
self.navigation = new BlockNavigation();
$(self.navigation).inject(self.content, "top");
},
defaultAction: function(action, params) {
var self = this;
if (self.current_page) {
self.current_page.hide();
if (self.current_page.list && self.current_page.list.navigation) self.current_page.list.navigation.dispose();
}
var route = new Route();
route.parse(action);
var page_name = route.getPage() != "index" ? route.getPage().capitalize() : self.default_page;
var page = self.sub_pages.filter(function(page) {
return page.name == page_name;
}).pick()["class"];
page.open(route.getAction() || "index", params);
page.show();
if (page.list && page.list.navigation) page.list.navigation.inject(self.navigation);
self.current_page = page;
self.navigation.activate(page_name.toLowerCase());
}
});
var BlockSearchMovieItem = 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;
var in_library;
if (info.in_library) {
in_library = [];
(info.in_library.releases || []).each(function(release) {
in_library.include(release.quality);
});
}
self.el = new Element("div.media_result", {
id: info.imdb,
events: {
click: self.showOptions.bind(self)
}
}).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"), self.data_container = new Element("div.data").grab(self.info_container = new Element("div.info").grab(new Element("h2", {
class: info.in_wanted && info.in_wanted.profile_id || in_library ? "in_library_wanted" : "",
title: self.getTitle()
}).adopt(self.title = new Element("span.title", {
text: self.getTitle()
}), self.year = info.year ? new Element("span.year", {
text: info.year
}) : null, info.in_wanted && info.in_wanted.profile_id ? new Element("span.in_wanted", {
text: "Already in wanted list: " + Quality.getProfile(info.in_wanted.profile_id).get("label")
}) : in_library ? new Element("span.in_library", {
text: "Already in library: " + in_library.join(", ")
}) : 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("movie.add", {
data: {
identifier: self.info.imdb,
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.grab(new Element("div.message", {
text: json.success ? "Movie successfully added." : "Movie didn't add properly. Check logs"
}));
self.mask.fade("out");
self.fireEvent("added");
},
onFailure: function() {
self.options_el.empty();
self.options_el.grab(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")) {
self.options_el.grab(new Element("div").adopt(new Element("div.title").grab(self.title_select = new Element("select", {
name: "title"
})), new Element("div.profile").grab(self.profile_select = new Element("select", {
name: "profile"
})), self.category_select_container = new Element("div.category").grab(self.category_select = new Element("select", {
name: "category"
}).grab(new Element("option", {
value: -1,
text: "None"
}))), new Element("div.add").grab(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_container.hide(); else {
self.category_select_container.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 MoviesWanted = new Class({
Extends: PageBase,
order: 10,
name: "wanted",
title: "Gimmy gimmy gimmy!",
folder_browser: null,
indexAction: function() {
var self = this;
if (!self.list) {
self.manual_search = new Element("a", {
title: "Force a search for the full wanted list",
text: "Search all wanted",
events: {
click: self.doFullSearch.bind(self, true)
}
});
self.scan_folder = new Element("a", {
title: "Scan a folder and rename all movies in it",
text: "Manual folder scan",
events: {
click: self.scanFolder.bind(self)
}
});
self.list = new MovieList({
identifier: "wanted",
status: "active",
actions: [ MA.IMDB, MA.Release, MA.Trailer, MA.Refresh, MA.Readd, MA.Delete, MA.Category, MA.Profile ],
add_new: true,
menu: [ self.manual_search, self.scan_folder ],
on_empty_element: App.createUserscriptButtons().addClass("empty_wanted")
});
$(self.list).inject(self.content);
self.startProgressInterval.delay(4e3, self);
}
},
doFullSearch: function() {
var self = this;
if (!self.search_in_progress) {
Api.request("movie.searcher.full_search");
self.startProgressInterval();
}
},
startProgressInterval: function() {
var self = this;
var start_text = self.manual_search.get("text");
self.progress_interval = setInterval(function() {
if (self.search_progress && self.search_progress.running) return;
self.search_progress = Api.request("movie.searcher.progress", {
onComplete: function(json) {
self.search_in_progress = true;
if (!json.movie) {
clearInterval(self.progress_interval);
self.search_in_progress = false;
self.manual_search.set("text", start_text);
} else {
var progress = json.movie;
self.manual_search.set("text", "Searching.. (" + Math.round((progress.total - progress.to_go) / progress.total * 100) + "%)");
}
}
});
}, 1e3);
},
scanFolder: function(e) {
e.stop();
var self = this;
var options = {
name: "Scan_folder"
};
if (!self.folder_browser) {
self.folder_browser = new Option.Directory("Scan", "folder", "", options);
self.folder_browser.save = function() {
var folder = self.folder_browser.getValue();
Api.request("renamer.scan", {
data: {
base_folder: folder
}
});
};
self.folder_browser.inject(self.content, "top");
self.folder_browser.fireEvent("injected");
self.folder_browser.directory_inlay.hide();
self.folder_browser.el.removeChild(self.folder_browser.el.firstChild);
self.folder_browser.showBrowser();
self.folder_browser.browser.getElements(".clear.button").hide();
self.folder_browser.save_button.text = "Select";
self.folder_browser.browser.setStyles({
"z-index": 1e3,
right: 20,
top: 0,
margin: 0
});
self.folder_browser.pointer.setStyles({
right: 20
});
} else {
self.folder_browser.showBrowser();
}
self.list.navigation_menu.hide();
}
});
var Charts = new Class({
Implements: [ Options, Events ],
shown_once: false,
initialize: function(options) {
var self = this;
self.setOptions(options);
self.create();
},
create: function() {
var self = this;
self.el = new Element("div.charts").adopt(self.el_no_charts_enabled = new Element("p.no_charts_enabled", {
html: "Hey, it looks like you have no charts enabled at the moment. If you'd like some great movie suggestions you can go to settings and turn on some charts of your choice.'
}), self.el_refresh_container = new Element("div.refresh").adopt(self.el_refresh_link = new Element("a.refresh.icon2", {
href: "#",
events: {
click: function(e) {
e.preventDefault();
self.el.getElements(".chart").destroy();
self.el_refreshing_text.show();
self.el_refresh_link.hide();
self.api_request = Api.request("charts.view", {
data: {
force_update: 1
},
onComplete: self.fill.bind(self)
});
}
}
}), self.el_refreshing_text = new Element("span.refreshing", {
text: "Refreshing charts..."
})));
self.show();
self.fireEvent.delay(0, self, "created");
},
fill: function(json) {
var self = this;
self.el_refreshing_text.hide();
self.el_refresh_link.show();
if (!json || json.count === 0) {
self.el_no_charts_enabled.show();
self.el_refresh_link.show();
self.el_refreshing_text.hide();
} else {
self.el_no_charts_enabled.hide();
json.charts.sort(function(a, b) {
return a.order - b.order;
});
Object.each(json.charts, function(chart) {
var chart_list = new MovieList({
navigation: false,
identifier: chart.name.toLowerCase().replace(/[^a-z0-9]+/g, "_"),
title: chart.name,
description: 'See source',
actions: [ MA.Add, MA.ChartIgnore, MA.IMDB, MA.Trailer ],
load_more: false,
view: "thumb",
force_view: true,
api_call: null
});
chart_list.store(chart.list);
chart_list.addMovies(chart.list, chart.list.length);
chart_list.checkIfEmpty();
chart_list.fireEvent("loaded");
$(chart_list).inject(self.el);
});
}
self.fireEvent("loaded");
},
show: function() {
var self = this;
self.el.show();
if (!self.shown_once) {
setTimeout(function() {
self.api_request = Api.request("charts.view", {
onComplete: self.fill.bind(self)
});
}, 100);
self.shown_once = true;
}
},
toElement: function() {
return this.el;
}
});
var TraktAutomation = new Class({
initialize: function() {
var self = this;
App.addEvent("loadSettings", self.addRegisterButton.bind(self));
},
addRegisterButton: function() {
var self = this, setting_page = App.getPage("Settings");
setting_page.addEvent("create", function() {
var fieldset = setting_page.tabs.automation.groups.trakt_automation, l = window.location;
var trakt_set = 0;
fieldset.getElements("input[type=text]").each(function(el) {
trakt_set += +(el.get("value") !== "");
});
new Element(".ctrlHolder").adopt(trakt_set > 0 ? [ self.unregister = new Element("a.button.red", {
text: "Unregister",
events: {
click: function() {
fieldset.getElements("input[name*=oauth_token]").set("value", "").fireEvent("change");
self.unregister.destroy();
self.unregister_or.destroy();
}
}
}), self.unregister_or = new Element("span[text=or]") ] : null, new Element("a.button", {
text: trakt_set > 0 ? "Register a different account" : "Register your trakt.tv account",
events: {
click: function() {
Api.request("automation.trakt.auth_url", {
data: {
host: l.protocol + "//" + l.hostname + (l.port ? ":" + l.port : "")
},
onComplete: function(json) {
window.location = json.url;
}
});
}
}
})).inject(fieldset);
});
}
});
new TraktAutomation();
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()
});
self.has_tab = true;
} else {
self.has_tab = false;
self.tab = null;
nav.removeTab("shows");
}
},
load: function() {
var self = this;
Api.request("settings", {
onComplete: function(json) {
self.toggleShows(json.values.shows.enabled);
}
});
App.on("shows.enabled", self.toggleShows.bind(self));
},
indexAction: function() {
var self = this;
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);
}
}).send();
setInterval(function() {
if (self.request && self.request.isRunning()) {
self.request.cancel();
self.startPoll();
}
}, 12e4);
},
startPoll: function() {
var self = this;
if (self.stopped) return;
if (self.request && self.request.isRunning()) self.request.cancel();
self.request = Api.request("nonblock/notification.listener", {
onSuccess: function(json) {
self.processData(json, false);
},
data: {
last_id: self.last_id
},
onFailure: function() {
self.startPoll.delay(2e3, self);
}
}).send();
},
stopPoll: function() {
if (this.request) this.request.cancel();
this.stopped = true;
},
processData: function(json, init) {
var self = this;
if (json && json.result) {
Array.each(json.result, function(result) {
App.trigger(result._t || result.type, [ result ]);
if (result.message && result.read === undefined && !init) self.showMessage(result.message);
});
if (json.result.length > 0) self.last_id = json.result.getLast().message_id;
}
self.startPoll.delay(1500, self);
},
showMessage: function(message, sticky, data) {
var self = this;
if (!self.message_container) self.message_container = new Element("div.messages").inject(document.body);
var new_message = new Element("div", {
class: "message" + (sticky ? " sticky" : ""),
html: '