PageRenderTime 54ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/share/spice/go_watch_it/go_watch_it.js

http://github.com/duckduckgo/zeroclickinfo-spice
JavaScript | 287 lines | 217 code | 35 blank | 35 comment | 67 complexity | 5aa03cf03c522d0bc696e4486c2d69d6 MD5 | raw file
Possible License(s): Apache-2.0
  1. (function (env) {
  2. "use strict";
  3. // Private functions, variables and helpers
  4. var streaming_providers = {
  5. "YouTube": 1,
  6. "Google Play": 1,
  7. "Disney Movies Anywhere": 1,
  8. "Target Ticket": 1,
  9. "Hulu": 1,
  10. "Crackle": 1,
  11. "Flixster": 1,
  12. "Netflix": 1
  13. };
  14. var purchase_providers = {
  15. "Best Buy": 1,
  16. "Walmart": 1,
  17. "Target": 1,
  18. "Fandango": 1,
  19. "Amazon": 1,
  20. "Redbox": 1
  21. };
  22. // For retina screen return optimized images @2x.png
  23. var append = ((DDG.is3x || DDG.is2x) ? "@2x.png" : ".png");
  24. var path = "assets/";
  25. var skip_providers = {
  26. // Movies on Demand
  27. "10": 1,
  28. // Filmmaker Direct Streaming
  29. "46": 1,
  30. // DirecTV
  31. "113": 1
  32. };
  33. var provider_icons = {
  34. "1": ['netflix', 'netflix'],
  35. "2": ['netflix', 'netflix'],
  36. "3": ['itunes', 'itunes-alt'],
  37. "4": ['fandango', 'fandango'],
  38. "5": ['vudu', 'vudu'],
  39. "7": ['amazon', 'amazon-alt'],
  40. "9": ['redbox', 'redbox'],
  41. "12": ['youtube', 'youtube-alt'],
  42. "13": ['sundance', 'sundance-alt'],
  43. "14": ['hulu', 'hulu'],
  44. "18": ['googleplay', 'googleplay-alt'],
  45. "19": ['xboxvideo', 'xboxvideo'],
  46. "20": ['sony', 'sony-alt'],
  47. "21": ['hulu', 'hulu'],
  48. "23": ['vhx', 'vhx-alt'],
  49. "31": ['bestbuy', 'bestbuy'],
  50. "32": ['walmart', 'walmart'],
  51. "36": ['target', 'target'],
  52. "37": ['targetticket', 'targetticket'],
  53. "64": ['flixster', 'flixster'],
  54. "65": ['crackle', 'crackle-alt'],
  55. "76": ['disney', 'disney-alt']
  56. };
  57. var zero_re = /\$0\.00/;
  58. var dollar_re = /\$/;
  59. env.ddg_spice_go_watch_it = function (api_result) {
  60. if (!api_result || api_result.error ||
  61. !DDG.getProperty(api_result, 'search.movies') ||
  62. !DDG.getProperty(api_result, 'search.shows') ||
  63. api_result.search.movies.length === 0 && api_result.search.shows.length === 0) {
  64. return Spice.failed('go_watch_it');
  65. }
  66. // We use this variable to find duplicate Netflix items
  67. // We'd want to merge them into one.
  68. var foundNetflix = false;
  69. var watchable,
  70. fallbacks = [],
  71. candidates = api_result.search.movies.concat(api_result.search.shows),
  72. skipArray = ["watch", "stream", "demand", "now", "online", "buy", "rent", "movie", "show", "tv"];
  73. // Find first relevant candidate
  74. // API result order gives precedence to movies
  75. //
  76. // TODO: Find a way to determine if TV show is more relevant choice
  77. $.each(candidates, function(index, obj){
  78. // skip if there are no available streaming sources.
  79. if (!obj.availabilities || obj.availabilities.length === 0) {
  80. return 1; //skip to next candidate
  81. }
  82. if (obj.availabilities.length === 1 &&
  83. obj.availabilities[0].provider_format_id in skip_providers) {
  84. return 1; //skip to next candidate
  85. }
  86. // Check for strict relevancy of the title
  87. // min word length: 3
  88. if (DDG.isRelevant(obj.title, skipArray, 3, true)) {
  89. watchable = obj;
  90. return false; //relevant result -- break out of loop
  91. }
  92. // Check for any relevancy of the title
  93. if (DDG.isRelevant(obj.title, skipArray, 3, false)) {
  94. //store for later, incase no highly relevant results
  95. fallbacks.push(obj);
  96. } else {
  97. return 1; //skip to next candidate;
  98. }
  99. });
  100. // only a mostly relevant result
  101. if (!watchable && fallbacks.length) {
  102. watchable = fallbacks[0];
  103. }
  104. // no relevant results
  105. if (!watchable) {
  106. return Spice.failed("go_watch_it");
  107. }
  108. Spice.add({
  109. id: "go_watch_it",
  110. name: "Watch",
  111. data: watchable.availabilities,
  112. meta: {
  113. sourceName: 'GoWatchIt',
  114. sourceUrl: 'https://gowatchit.com' + (watchable.url ? watchable.url : ""),
  115. primaryText: 'Providers for ' + watchable.title + " (" + watchable.year + ")"
  116. },
  117. normalize: function (item) {
  118. if (item.provider_format_id in skip_providers) {
  119. return null;
  120. }
  121. // Try to strip anything that comes before the price.
  122. if(/\$/.test(item.buy_line) || /\$/.test(item.rent_line)) {
  123. item.buy_line = item.buy_line.replace(/^[a-z ]+/i, "");
  124. item.rent_line = item.rent_line.replace(/^[a-z ]+/i, "");
  125. }
  126. // Change the format line to match the other tiles.
  127. if (item.format_line === "DVD & Blu-ray") {
  128. item.format_line = "DVD / Blu-ray";
  129. }
  130. // If the provider is "Netflix Mail" Change buy_line and format_line
  131. if (item.provider_format_name === "Netflix Mail" && item.category !== "online") {
  132. item.format_line = "Available on Blu-ray / DVD";
  133. }
  134. // Only return a single Netflix item
  135. if (item.provider_name === "Netflix") {
  136. if (!foundNetflix) {
  137. foundNetflix = true;
  138. } else if (item.category !== "online") {
  139. return null;
  140. }
  141. }
  142. // Replace the icon if we can.
  143. if (item.provider_format_id in provider_icons) {
  144. item.provider_format_logos_names = provider_icons[item.provider_format_id];
  145. item.provider_format_logos.dark = DDG.get_asset_path('go_watch_it', path + item.provider_format_logos_names[0]) + append;
  146. item.provider_format_logos.light = DDG.get_asset_path('go_watch_it', path + item.provider_format_logos_names[1]) + append;
  147. } else {
  148. return null;
  149. }
  150. return {
  151. url: item.watch_now_url
  152. };
  153. },
  154. templates: {
  155. item: 'base_item',
  156. options: {
  157. content: Spice.go_watch_it.content
  158. },
  159. variants: {
  160. tile: 'narrow'
  161. }
  162. }
  163. });
  164. };
  165. // Display something even if we don't have any price
  166. Spice.registerHelper("gwi_fallbackText", function(options) {
  167. var message = "Available";
  168. var rent_re = /rent_own/;
  169. var subscription_re = /subscription/;
  170. if(this.categories) {
  171. this.categories.forEach(function(elem) {
  172. if(rent_re.test(elem)) {
  173. message = 'Available for Purchase or Rent';
  174. }
  175. if(subscription_re.test(elem)) {
  176. message = 'Available with Subscription';
  177. }
  178. });
  179. }
  180. // If the provider is in this hash, it means that they sell it
  181. // but they don't have the actual price to display.
  182. if (this.provider_name in purchase_providers && this.buy_line === "") {
  183. message = "Available for Purchase";
  184. }
  185. // If the provider is "Netflix Mail" Change buy_line and format_line
  186. if (this.provider_format_name === "Netflix Mail" && this.category !== "online") {
  187. message = "Available for Rent";
  188. }
  189. // Fandango has its own suggested line
  190. if (this.provider_name === "Fandango") {
  191. message = this.suggested_line;
  192. }
  193. // If the provider is in this hash, it means that they provide
  194. // streaming if they don't buy or sell stuff.
  195. if (this.provider_name in streaming_providers && this.buy_line === "" && this.rent_line === "") {
  196. message = "Available for Streaming";
  197. }
  198. return message;
  199. });
  200. // Display the footer properly and show something if all fails.
  201. Spice.registerHelper('gwi_footer', function(format_line) {
  202. if(!format_line) {
  203. var quality = [];
  204. for(var format in this.formats) {
  205. if(quality.indexOf(this.formats[format].quality) === -1) {
  206. quality.push(this.formats[format].quality);
  207. }
  208. }
  209. if(quality.length > 0) {
  210. format_line = "Available in " + quality.join(" / ");
  211. } else {
  212. format_line = "Available";
  213. }
  214. }
  215. return format_line;
  216. });
  217. Spice.registerHelper("gwi_buyOrRent", function (buy_line, rent_line, options) {
  218. if (buy_line && buy_line !== "" && !zero_re.test(buy_line) && dollar_re.test(buy_line)) {
  219. this.rent_line = "";
  220. } else if(rent_line && rent_line !== "" && !zero_re.test(rent_line) && dollar_re.test(rent_line)) {
  221. this.buy_line = "";
  222. } else {
  223. return options.inverse(this);
  224. }
  225. return options.fn(this);
  226. });
  227. // Check to see if both buy_line and rent_line are present.
  228. Spice.registerHelper("gwi_ifHasBothBuyAndRent", function (buy_line, rent_line, options) {
  229. if (buy_line && buy_line !== "" && rent_line && rent_line !== "" && !zero_re.test(buy_line) && !zero_re.test(rent_line) && dollar_re.test(buy_line) && dollar_re.test(rent_line)) {
  230. return options.fn(this);
  231. } else {
  232. return options.inverse(this);
  233. }
  234. });
  235. // Grab dollar amount from 'Rent from $X.XX' string.
  236. Spice.registerHelper("gwi_price", function (line, options) {
  237. if(dollar_re.test(line)) {
  238. var strings = line.split("$");
  239. return "$" + strings[strings.length - 1];
  240. }
  241. return line;
  242. });
  243. // Get the class for the footer element based on whether or not both the buy_line
  244. // and rent_line are present.
  245. Spice.registerHelper("gwi_footerClass", function (buy_line, rent_line, options) {
  246. var klass = 'gwi-footer';
  247. if (buy_line && buy_line !== '' && rent_line && rent_line !== '') {
  248. klass += ' double';
  249. }
  250. return klass;
  251. });
  252. }(this));