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");
}
requestTimeout(function() {
message.destroy();
}, 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) clearRequestTimeout(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) clearRequestTimeout(self.timer);
self.timer = requestTimeout(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(App.getBlock("footer"));
},
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() {
requestTimeout(function() {
App.checkAvailable(1e3, function() {
window.location.reload();
});
}, 500);
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 BlockSearch = new Class({
Extends: BlockBase,
options: {
animate: true
},
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) clearRequestTimeout(focus_timer);
if (this.get("value")) self.hideResults(false);
},
blur: function() {
focus_timer = requestTimeout(function() {
self.el.removeClass("focused");
self.last_q = null;
}, 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");
if (self.options.animate) {
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) clearRequestTimeout(self.autocomplete_timer);
self.autocomplete_timer = requestTimeout(self.autocomplete.bind(self), 300);
}
},
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) {
requestTimeout(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");
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"))));
var eta_date = parent.getETA("%b %Y");
self.addSection("description", new Element("div").adopt(new Element("div", {
text: parent.get("plot")
}), new Element("div.meta", {
html: (eta_date ? "ETA:" + eta_date + "" : "") + "" + (parent.get("genres") || []).join(", ") + ""
})));
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");
self.outer_click = function() {
self.close();
};
App.addEvent("history.push", self.outer_click);
},
close: function() {
var self = this;
var ended = function() {
self.el.dispose();
self.overlay.removeEventListener("transitionend", ended);
};
self.overlay.addEventListener("transitionend", ended, false);
self.el.removeClass("show");
App.removeEvent("history.push", self.outer_click);
}
});
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
},
available_views: [ "thumb", "list" ],
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);
},
"change:relay(.movie input)": function(e, el) {
e.stopPropagation();
el = el.getParent(".movie");
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 === 1 ? "1 movie" : (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;
if (self.available_views.indexOf(new_view) == -1) new_view = "thumb";
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) clearRequestTimeout(self.search_timer);
self.search_timer = requestTimeout(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;
}, 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 = requestTimeout(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) clearRequestTimeout(loader_timeout);
if (self.loader_first) {
var lf = self.loader_first;
self.loader_first = null;
lf.removeClass("show");
requestTimeout(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) {
var ee = typeOf(self.options.on_empty_element) == "function" ? self.options.on_empty_element() : self.options.on_empty_element;
ee.inject(self.loader_first || self.title || self.movie_list, "after");
if (self.navigation) self.navigation.hide();
self.empty_element = ee;
} 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 = requestInterval(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) {
clearRequestInterval(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, "top");
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(".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.container = new Element("div.trailer_container").adopt(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.background = new Element("div.background"));
requestTimeout(function() {
var url = data_url.substitute({
title: encodeURI(self.getTitle()),
year: self.get("year")
});
new Request.JSONP({
url: url,
onComplete: function(json) {
if (json.items.length > 0) {
self.video_id = json.items[0].id.videoId;
self.background.setStyle("background-image", "url(" + json.items[0].snippet.thumbnails.high.url + ")");
self.background.addClass("visible");
} else {
self.container.getParent(".section").addClass("no_trailer");
}
}
}).send();
}, 1e3);
}
return self.container;
},
watch: function() {
var self = this;
new Element("iframe", {
src: "https://www.youtube-nocookie.com/embed/" + self.video_id + "?rel=0&showinfo=0&autoplay=1&showsearch=0&iv_load_policy=3&vq=hd720"
}).inject(self.container);
}
});
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;
if (!self.movie.data.releases || self.movie.data.releases.length === 0) return;
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").grab(self.inner = new Element("div.inner"));
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);
requestTimeout(self.details.open.bind(self.details), 20);
},
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);
requestTimeout(function() {
self.update(notification);
}, 2e3);
};
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) {
requestTimeout(function() {
if (self.spinner) {
self.mask.fade("out");
requestTimeout(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.inner.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;
self.el.addClass("status_" + self.get("status"));
var eta_date = self.getETA();
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.inner.adopt(self.select_checkbox = new Element("input[type=checkbox]"), new Element("div.poster_container").adopt(thumbnail, self.actions_el = new Element("div.actions")), new Element("div.info").adopt(new Element("div.title").adopt(new Element("span", {
text: self.getTitle() || "n/a"
}), new Element("div.year", {
text: self.data.info.year || "n/a"
})), eta_date ? new Element("div.eta", {
text: eta_date,
title: "ETA"
}) : null, self.quality = new Element("div.quality"), rating ? new Element("div.rating[title=" + rating[0] + "]").adopt(stars, new Element("span.votes[text=(" + rating.join(" / ") + ")][title=Votes]")) : null));
if (!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.addActions();
self.openDetails();
}
},
addActions: 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);
});
}
},
onMouseenter: function() {
var self = this;
if (App.mobile_screen) return;
self.addActions();
if (self.list.current_view == "thumb") {
self.el.addClass("hover_start");
requestTimeout(function() {
self.el.removeClass("hover_start");
}, 300);
dynamics.css(self.inner, {
scale: 1
});
dynamics.animate(self.inner, {
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
});
});
}
},
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");
},
getETA: function(format) {
var self = this, d = new Date(), now = Math.round(+d / 1e3), eta = null, eta_date = "";
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 = format ? eta_date.format(format) : eta_date.format("%b") + (d.getFullYear() != eta_date.getFullYear() ? " " + eta_date.getFullYear() : "");
}
}
return now + 8035200 > eta ? eta_date : "";
},
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.el);
},
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: "Gimme gimme gimme!",
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.MarkAsDone, 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: function() {
return new Element("div.empty_wanted").adopt(new Element("div.no_movies", {
text: "Seems like you don't have any movies yet.. Maybe add some via search or the extension."
}), App.createUserscriptButtons());
}
});
$(self.list).inject(self.content);
requestTimeout(self.startProgressInterval.bind(self), 4e3);
}
},
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 = requestInterval(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) {
clearRequestInterval(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").grab(self.el_refresh_container = new Element("div.refresh").grab(self.el_refreshing_text = new Element("span.refreshing", {
text: "Refreshing charts..."
})));
self.show();
requestTimeout(function() {
self.fireEvent("created");
}, 0);
},
fill: function(json) {
var self = this;
self.el_refreshing_text.hide();
if (json && json.count > 0) {
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) {
requestTimeout(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();
var NotificationBase = new Class({
Extends: BlockBase,
Implements: [ Options, Events ],
stopped: false,
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();
requestTimeout(function() {
self.startInterval();
}, $(window).getSize().x <= 480 ? 2e3 : 100);
});
},
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();
requestInterval(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() {
requestTimeout(self.startPoll.bind(self), 2e3);
}
}).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;
}
requestTimeout(self.startPoll.bind(self), 1500);
},
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: '