upload.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. /**
  2. * @description Takes care of every action an album can handle and execute.
  3. */
  4. upload = {}
  5. upload.show = function(title, files, callback) {
  6. basicModal.show({
  7. body: build.uploadModal(title, files),
  8. buttons: {
  9. action: {
  10. title: 'Close',
  11. class: 'hidden',
  12. fn: basicModal.close
  13. }
  14. },
  15. callback
  16. })
  17. }
  18. upload.notify = function(title, text) {
  19. if (text==null||text==='') text = 'You can now manage your new photo(s).'
  20. if (!window.webkitNotifications) return false
  21. if (window.webkitNotifications.checkPermission()!==0) window.webkitNotifications.requestPermission()
  22. if (window.webkitNotifications.checkPermission()===0 && title) {
  23. let popup = window.webkitNotifications.createNotification('', title, text)
  24. popup.show()
  25. }
  26. }
  27. upload.start = {
  28. local: function(files) {
  29. let albumID = album.getID()
  30. let error = false
  31. let warning = false
  32. const process = function(files, file) {
  33. let formData = new FormData()
  34. let xhr = new XMLHttpRequest()
  35. let pre_progress = 0
  36. let progress = 0
  37. let next_file_started = false
  38. const finish = function() {
  39. window.onbeforeunload = null
  40. $('#upload_files').val('')
  41. if (error===false && warning===false) {
  42. // Success
  43. basicModal.close()
  44. upload.notify('Upload complete')
  45. } else if (error===false && warning===true) {
  46. // Warning
  47. $('.basicModal #basicModal__action.hidden').show()
  48. upload.notify('Upload complete')
  49. } else {
  50. // Error
  51. $('.basicModal #basicModal__action.hidden').show()
  52. upload.notify('Upload complete', 'Failed to upload one or more photos.')
  53. }
  54. albums.refresh()
  55. if (album.getID()===false) lychee.goto('0')
  56. else album.load(albumID)
  57. }
  58. formData.append('function', 'Photo::add')
  59. formData.append('albumID', albumID)
  60. formData.append(0, file)
  61. xhr.open('POST', api.path)
  62. xhr.onload = function() {
  63. let data = null
  64. let wait = false
  65. let errorText = ''
  66. const isNumber = (n) => (!isNaN(parseFloat(n)) && isFinite(n))
  67. try {
  68. data = JSON.parse(xhr.responseText)
  69. } catch(e) {
  70. data = ''
  71. }
  72. file.ready = true
  73. // Set status
  74. if (xhr.status===200 && isNumber(data)) {
  75. // Success
  76. $('.basicModal .rows .row:nth-child(' + (file.num + 1) + ') .status')
  77. .html('Finished')
  78. .addClass('success')
  79. } else {
  80. if (data.substr(0, 6)==='Error:') {
  81. errorText = data.substr(6) + ' Please take a look at the console of your browser for further details.'
  82. error = true
  83. // Error Status
  84. $('.basicModal .rows .row:nth-child(' + (file.num + 1) + ') .status')
  85. .html('Failed')
  86. .addClass('error')
  87. // Throw error
  88. if (error===true) lychee.error('Upload failed. Server returned an error!', xhr, data)
  89. } else if (data.substr(0, 8)==='Warning:') {
  90. errorText = data.substr(8)
  91. warning = true
  92. // Warning Status
  93. $('.basicModal .rows .row:nth-child(' + (file.num + 1) + ') .status')
  94. .html('Skipped')
  95. .addClass('warning')
  96. // Throw error
  97. if (error===true) lychee.error('Upload failed. Server returned a warning!', xhr, data)
  98. } else {
  99. errorText = 'Server returned an unknown response. Please take a look at the console of your browser for further details.'
  100. error = true
  101. // Error Status
  102. $('.basicModal .rows .row:nth-child(' + (file.num + 1) + ') .status')
  103. .html('Failed')
  104. .addClass('error')
  105. // Throw error
  106. if (error===true) lychee.error('Upload failed. Server returned an unkown error!', xhr, data)
  107. }
  108. $('.basicModal .rows .row:nth-child(' + (file.num + 1) + ') p.notice')
  109. .html(errorText)
  110. .show()
  111. }
  112. // Check if there are file which are not finished
  113. for (let i = 0; i < files.length; i++) {
  114. if (files[i].ready===false) {
  115. wait = true
  116. break
  117. }
  118. }
  119. // Finish upload when all files are finished
  120. if (wait===false) finish()
  121. }
  122. xhr.upload.onprogress = function(e) {
  123. if (e.lengthComputable!==true) return false
  124. // Calculate progress
  125. progress = (e.loaded / e.total * 100 | 0)
  126. // Set progress when progress has changed
  127. if (progress>pre_progress) {
  128. $('.basicModal .rows .row:nth-child(' + (file.num+1) + ') .status').html(progress + '%')
  129. pre_progress = progress
  130. }
  131. if (progress>=100 && next_file_started===false) {
  132. // Scroll to the uploading file
  133. let scrollPos = 0
  134. if ((file.num + 1)>4) scrollPos = (file.num + 1 - 4) * 40
  135. $('.basicModal .rows').scrollTop(scrollPos)
  136. // Set status to processing
  137. $('.basicModal .rows .row:nth-child(' + (file.num + 1) + ') .status').html('Processing')
  138. // Upload next file
  139. if (file.next!=null) {
  140. process(files, file.next)
  141. next_file_started = true
  142. }
  143. }
  144. }
  145. xhr.send(formData)
  146. }
  147. if (files.length<=0) return false
  148. if (albumID===false || visible.albums()===true) albumID = 0
  149. for (let i = 0; i < files.length; i++) {
  150. files[i].num = i
  151. files[i].ready = false
  152. if (i < files.length-1) files[i].next = files[i + 1]
  153. else files[i].next = null
  154. }
  155. window.onbeforeunload = function() { return 'Lychee is currently uploading!' }
  156. upload.show('Uploading', files, function() {
  157. // Upload first file
  158. process(files, files[0])
  159. })
  160. },
  161. url: function(url = '') {
  162. let albumID = album.getID()
  163. url = (typeof url === 'string' ? url : '')
  164. if (albumID===false) albumID = 0
  165. const action = function(data) {
  166. let files = []
  167. if (data.link && data.link.length>3) {
  168. basicModal.close()
  169. files[0] = {
  170. name: data.link
  171. }
  172. upload.show('Importing URL', files, function() {
  173. $('.basicModal .rows .row .status').html('Importing')
  174. let params = {
  175. url: data.link,
  176. albumID
  177. }
  178. api.post('Import::url', params, function(data) {
  179. // Same code as in import.dropbox()
  180. if (data!==true) {
  181. $('.basicModal .rows .row p.notice')
  182. .html('The import has been finished, but returned warnings or errors. Please take a look at the log (Settings -> Show Log) for further details.')
  183. .show()
  184. $('.basicModal .rows .row .status')
  185. .html('Finished')
  186. .addClass('warning')
  187. // Show close button
  188. $('.basicModal #basicModal__action.hidden').show()
  189. // Log error
  190. lychee.error(null, params, data)
  191. } else {
  192. basicModal.close()
  193. }
  194. upload.notify('Import complete')
  195. albums.refresh()
  196. if (album.getID()===false) lychee.goto('0')
  197. else album.load(albumID)
  198. })
  199. })
  200. } else basicModal.error('link')
  201. }
  202. basicModal.show({
  203. body: lychee.html`<p>Please enter the direct link to a photo to import it: <input class='text' name='link' type='text' placeholder='http://' value='$${ url }'></p>`,
  204. buttons: {
  205. action: {
  206. title: 'Import',
  207. fn: action
  208. },
  209. cancel: {
  210. title: 'Cancel',
  211. fn: basicModal.close
  212. }
  213. }
  214. })
  215. },
  216. server: function() {
  217. let albumID = album.getID()
  218. if (albumID===false) albumID = 0
  219. const action = function(data) {
  220. let files = []
  221. files[0] = {
  222. name: data.path
  223. }
  224. upload.show('Importing from server', files, function() {
  225. $('.basicModal .rows .row .status').html('Importing')
  226. let params = {
  227. albumID,
  228. path: data.path
  229. }
  230. api.post('Import::server', params, function(data) {
  231. albums.refresh()
  232. upload.notify('Import complete')
  233. if (data==='Notice: Import only contained albums!') {
  234. // No error, but the folder only contained albums
  235. // Go back to the album overview to show the imported albums
  236. if (visible.albums()) lychee.load()
  237. else lychee.goto()
  238. basicModal.close()
  239. return true
  240. } else if (data==='Warning: Folder empty or no readable files to process!') {
  241. // Error because the import could not start
  242. $('.basicModal .rows .row p.notice')
  243. .html('Folder empty or no readable files to process. Please take a look at the log (Settings -> Show Log) for further details.')
  244. .show()
  245. $('.basicModal .rows .row .status')
  246. .html('Failed')
  247. .addClass('error')
  248. // Log error
  249. lychee.error('Could not start import because the folder was empty!', params, data)
  250. } else if (data!==true) {
  251. // Maybe an error, maybe just some skipped photos
  252. $('.basicModal .rows .row p.notice')
  253. .html('The import has been finished, but returned warnings or errors. Please take a look at the log (Settings -> Show Log) for further details.')
  254. .show()
  255. $('.basicModal .rows .row .status')
  256. .html('Finished')
  257. .addClass('warning')
  258. // Log error
  259. lychee.error(null, params, data)
  260. } else {
  261. // No error, everything worked fine
  262. basicModal.close()
  263. }
  264. if (album.getID()===false) lychee.goto('0')
  265. else album.load(albumID)
  266. // Show close button
  267. $('.basicModal #basicModal__action.hidden').show()
  268. })
  269. })
  270. }
  271. basicModal.show({
  272. body: lychee.html`<p>This action will import all photos, folders and sub-folders which are located in the following directory. The <b>original files will be deleted</b> after the import when possible. <input class='text' name='path' type='text' maxlength='100' placeholder='Absolute path to directory' value='$${ lychee.location }uploads/import/'></p>`,
  273. buttons: {
  274. action: {
  275. title: 'Import',
  276. fn: action
  277. },
  278. cancel: {
  279. title: 'Cancel',
  280. fn: basicModal.close
  281. }
  282. }
  283. })
  284. },
  285. dropbox: function() {
  286. let albumID = album.getID()
  287. if (albumID===false) albumID = 0
  288. const success = function(files) {
  289. let links = ''
  290. for (let i = 0; i < files.length; i++) {
  291. links += files[i].link + ','
  292. files[i] = {
  293. name : files[i].link
  294. }
  295. }
  296. // Remove last comma
  297. links = links.substr(0, links.length - 1)
  298. upload.show('Importing from Dropbox', files, function() {
  299. $('.basicModal .rows .row .status').html('Importing')
  300. let params = {
  301. url: links,
  302. albumID
  303. }
  304. api.post('Import::url', params, function(data) {
  305. // Same code as in import.url()
  306. if (data!==true) {
  307. $('.basicModal .rows .row p.notice')
  308. .html('The import has been finished, but returned warnings or errors. Please take a look at the log (Settings -> Show Log) for further details.')
  309. .show()
  310. $('.basicModal .rows .row .status')
  311. .html('Finished')
  312. .addClass('warning')
  313. // Show close button
  314. $('.basicModal #basicModal__action.hidden').show()
  315. // Log error
  316. lychee.error(null, params, data)
  317. } else {
  318. basicModal.close()
  319. }
  320. upload.notify('Import complete')
  321. albums.refresh()
  322. if (album.getID()===false) lychee.goto('0')
  323. else album.load(albumID)
  324. })
  325. })
  326. }
  327. lychee.loadDropbox(function() {
  328. Dropbox.choose({
  329. linkType: 'direct',
  330. multiselect: true,
  331. success
  332. })
  333. })
  334. }
  335. }