diff --git a/README.md b/README.md index 7d829eb033..6381be0846 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Want to report a bug, request a feature, contribute to or translate Butter? Chec If you're comfortable getting up and running from a `git clone`, this method is for you. -After you clone the GitHub repository, you will need to build a number of assets using grunt. +After you clone the GitHub repository, you will need to build a number of assets using gulp. The [master](https://github.com/butterproject/butter-desktop) branch which contains the latest release. @@ -41,7 +41,7 @@ Keep track of Butter development and community activity. * Join in discussions on the [Butter Forum](https://www.reddit.com/r/ButterProject) * Connect with us on IRC at `#butterproject` on freenode ([web access](http://webchat.freenode.net/?channels=butterproject)) -##Screenshots +## Screenshots ![Butter](https://cloud.githubusercontent.com/assets/8317250/10714437/b1e1dc8c-7b32-11e5-9c25-d9fbd5b2f3bd.png) ![Debugging Butter](https://cloud.githubusercontent.com/assets/8317250/10714430/add70234-7b32-11e5-9be7-1de539d865ba.png) diff --git a/gulpfile.js b/gulpfile.js index 66768afb97..24056a06b4 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -62,7 +62,7 @@ const parsePlatforms = () => { // returns an array of paths with the node_modules to include in builds const parseReqDeps = () => { return new Promise((resolve, reject) => { - exec('npm ls --production=true --parseable=true', (error, stdout, stderr) => { + exec('npm ls --production=true --parseable=true', {maxBuffer: 1024 * 500}, (error, stdout, stderr) => { if (error || stderr) { reject(error || stderr); } else { diff --git a/package.json b/package.json index afbfc28ba4..c6f62bf218 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ }, "chromium-args": "--enable-node-worker", "dependencies": { - "butter-provider": "git+https://github.com/butterproviders/butter-provider", + "butter-provider": "0.6.0", "butter-provider-ccc": "git+https://github.com/butterproviders/butter-provider-ccc", "butter-provider-youtube": "git+https://github.com/butterproviders/butter-provider-youtube", "butter-provider-vodo": "git+https://github.com/butterproviders/butter-provider-vodo", @@ -45,7 +45,7 @@ "async": "2.x.x", "bonjour": "^3.5.0", "chromecasts": "1.9.0", - "defer-request": "0.0.2", + "defer-request": "0.0.3", "dlnacasts": "0.1.0", "i18n": "0.x.x", "iconv-lite": "0.x.x", @@ -67,15 +67,15 @@ "rimraf": "2.x.x", "sanitizer": "0.x.x", "semver": "5.x.x", - "send": "0.14.1", + "send": "0.14.2", "strike-api": "0.2.0", "tar": "2.2.1", "temp": "0.x.x", "webtorrent-health": "1.x.x", - "trakt.tv": "2.x.x", - "trakt.tv-ondeck": "0.x.x", - "trakt.tv-matcher": "1.x.x", - "trakt.tv-images": "1.x.x", + "trakt.tv": "5.0.1", + "trakt.tv-ondeck": "5.0.1", + "trakt.tv-matcher": "5.0.0", + "trakt.tv-images": "5.0.0", "underscore": "1.x.x", "urijs": "1.x.x", "webtorrent": "^0.98.0" diff --git a/src/app/app.js b/src/app/app.js index 1ef2c3bc6c..26d91cde11 100644 --- a/src/app/app.js +++ b/src/app/app.js @@ -100,6 +100,7 @@ App.addInitializer(function (options) { var width = parseInt(localStorage.width ? localStorage.width : Settings.defaultWidth); var height = parseInt(localStorage.height ? localStorage.height : Settings.defaultHeight); + var isMaximized = Boolean(parseInt(localStorage.isMaximized)); var x = parseInt(localStorage.posX ? localStorage.posX : -1); var y = parseInt(localStorage.posY ? localStorage.posY : -1); @@ -128,8 +129,13 @@ App.addInitializer(function (options) { } win.zoomLevel = zoom; - win.resizeTo(width, height); - win.moveTo(x, y); + + if (isMaximized) { + win.maximize(); + } else { + win.resizeTo(width, height); + win.moveTo(x, y); + } }); var initTemplates = function () { @@ -152,7 +158,9 @@ var initApp = function () { var mainWindow = new App.View.MainWindow(); // -m argument to open minimized to tray - if (nw.App.fullArgv.indexOf('-m') === -1) { + var isStartMinimized = nw.App.fullArgv.indexOf('-m') !== -1; + + if (!isStartMinimized) { win.show(); } @@ -213,35 +221,6 @@ var delCache = function () { win.close(true); }; -win.on('resize', function (width, height) { - localStorage.width = Math.round(width); - localStorage.height = Math.round(height); -}); - -win.on('move', function (x, y) { - localStorage.posX = Math.round(x); - localStorage.posY = Math.round(y); -}); - -win.on('enter-fullscreen', function () { - App.vent.trigger('window:focus'); -}); - -// Wipe the tmpFolder when closing the app (this frees up disk space) -win.on('close', function () { - if (App.settings.deleteTmpOnClose) { - deleteFolder(App.settings.tmpLocation); - } - if (fs.existsSync(path.join(data_path, 'logs.txt'))) { - fs.unlinkSync(path.join(data_path, 'logs.txt')); - } - try { - delCache(); - } catch (e) { - win.close(true); - } -}); - String.prototype.capitalize = function () { return this.charAt(0).toUpperCase() + this.slice(1); }; @@ -656,6 +635,60 @@ if (nw.App.fullArgv.indexOf('-f') !== -1) { win.enterFullscreen(); } +// nwjs window events +win.on('focus', function () { //hack to make it somehow work + win.setAlwaysOnTop(true); + win.setAlwaysOnTop(Settings.alwaysOnTop); +}); + +win.on('resize', function (width, height) { + localStorage.width = Math.round(width); + localStorage.height = Math.round(height); +}); + +win.on('move', function (x, y) { + localStorage.posX = Math.round(x); + localStorage.posY = Math.round(y); +}); + +win.on('enter-fullscreen', function () { + win.focus(); +}); + +win.on('minimize', function () { + if (Settings.minimizeToTray) { + minimizeToTray(); + } +}); + +win.on('maximize', function () { + localStorage.isMaximized = 1; + $('.os-max').addClass('os-is-max'); +}); + +win.on('restore', function () { + if (Boolean(parseInt(localStorage.isMaximized))) { + localStorage.isMaximized = 0; + } + + $('.os-max').removeClass('os-is-max'); +}); + +// Wipe the tmpFolder when closing the app (this frees up disk space) +win.on('close', function () { + if (App.settings.deleteTmpOnClose) { + deleteFolder(App.settings.tmpLocation); + } + if (fs.existsSync(path.join(data_path, 'logs.txt'))) { + fs.unlinkSync(path.join(data_path, 'logs.txt')); + } + try { + delCache(); + } catch (e) { + win.close(true); + } +}); + nw.App.on('open', function (cmd) { var file; if (os.platform() === 'win32') { @@ -682,19 +715,6 @@ nw.App.on('open', function (cmd) { } }); -win.on('minimize', function () { - if (Settings.minimizeToTray) { - minimizeToTray(); - } -}); - -// When win.focus() doesn't do it's job right, play dirty. -App.vent.on('window:focus', function () { - win.setAlwaysOnTop(true); - win.focus(); - win.setAlwaysOnTop(Settings.alwaysOnTop); -}); - // On uncaught exceptions, log to console. process.on('uncaughtException', function (err) { try { diff --git a/src/app/images/icons/topbar_sprite_win.png b/src/app/images/icons/topbar_sprite_win.png index cec88dfc9f..dec4891930 100644 Binary files a/src/app/images/icons/topbar_sprite_win.png and b/src/app/images/icons/topbar_sprite_win.png differ diff --git a/src/app/lib/providers/opensubtitles.js b/src/app/lib/providers/opensubtitles.js index 5f596824c3..6d155485ac 100644 --- a/src/app/lib/providers/opensubtitles.js +++ b/src/app/lib/providers/opensubtitles.js @@ -7,7 +7,8 @@ openSRT = new OS({ useragent: Settings.opensubtitles.useragent + ' v' + (Settings.version || 1), username: Settings.opensubtitlesUsername, - password: Settings.opensubtitlesPassword + password: Settings.opensubtitlesPassword, + ssl: true }); }; diff --git a/src/app/lib/providers/trakttv.js b/src/app/lib/providers/trakttv.js index 2ce8df95b4..4daf8a5cfb 100644 --- a/src/app/lib/providers/trakttv.js +++ b/src/app/lib/providers/trakttv.js @@ -5,7 +5,11 @@ this.client = new Trakt({ client_id: Settings.trakttv.client_id, client_secret: Settings.trakttv.client_secret, - plugins: ['ondeck', 'matcher', 'images'], + plugins: { + ondeck: require('trakt.tv-ondeck'), + matcher: require('trakt.tv-matcher'), + images: require('trakt.tv-images') + }, options: { images: { smallerImages: true, diff --git a/src/app/lib/vendor/videojshooks.js b/src/app/lib/vendor/videojshooks.js index 42712ec6ed..986fec4392 100644 --- a/src/app/lib/vendor/videojshooks.js +++ b/src/app/lib/vendor/videojshooks.js @@ -284,6 +284,8 @@ vjs.TextTrack.prototype.load = function () { .replace(/(- |==|sync).*[\s\S].*[\s\S].*[\s\S].*[\s\S].*\.(com|org|net|edu)/ig, '') // various teams .replace(/[^0-9][\s\S][^0-9\W].*[\s\S].*[\s\S].*opensubtitles.*/ig, ''); // opensubs "contact us" ads + strings = Common.sanitize(strings); // xss-style attacks + strings = strings.replace(/--\>\;/g, '-->'); // restore srt format callback(strings); }; diff --git a/src/app/lib/views/browser/item.js b/src/app/lib/views/browser/item.js index 477af06e35..f03d939393 100644 --- a/src/app/lib/views/browser/item.js +++ b/src/app/lib/views/browser/item.js @@ -18,6 +18,7 @@ ui: { covers: '.cover-imgs', + defaultCover: '.cover', bookmarkIcon: '.actions-favorites', watchedIcon: '.actions-watched' }, @@ -135,7 +136,7 @@ var posterCache = new Image(); posterCache.src = poster; - this.ui.covers.append(``); + this.ui.covers.append(`
`); posterCache.onload = function () { posterCache.onload = () => {}; @@ -149,11 +150,14 @@ posterCache.src = c.toDataURL(); } - this.ui.covers.children(-1).attr('src', posterCache.src).addClass('fadein'); + this.ui.covers.children(-1).css('background-image', 'url('+posterCache.src+')').addClass('fadein').delay(600).queue(_ => { + this.ui.defaultCover.addClass('empty'); + }); }.bind(this); posterCache.onerror = function (e) { this.ui.covers.empty(); + this.ui.defaultCover.removeClass('empty'); }.bind(this); }, diff --git a/src/app/lib/views/browser/list.js b/src/app/lib/views/browser/list.js index abe98c6883..d7d51b83ac 100644 --- a/src/app/lib/views/browser/list.js +++ b/src/app/lib/views/browser/list.js @@ -262,9 +262,9 @@ onLoaded: function () { App.vent.trigger('list:loaded'); - this.checkEmpty(); var self = this; - this.addloadmore(); + + this.completerow(); if (typeof (this.ui.spinner) === 'object') { this.ui.spinner.hide(); @@ -298,23 +298,34 @@ } }, + completerow: function () { + var elms = this.addloadmore(); + elms += this.addghosts(); + + $('.ghost, #load-more-item').remove(); + $('.items').append(elms); + + this.showloadmore(); + }, + + addghosts: function () { + return '
'.repeat(10); + }, + addloadmore: function () { - var self = this; + return '
' + i18n.__('Load More') + '
'; + }, - // maxResults to hide load-more on providers that return hasMore=true no matter what. - var currentPage = Math.ceil(this.collection.length / 50); - var maxResults = currentPage * 50; + showloadmore: function () { + var self = this; switch (App.currentview) { case 'movies': case 'shows': case 'anime': if ($('.items').children().last().attr('id') !== 'load-more-item') { - $('#load-more-item').remove(); if (this.collection.hasMore && !this.collection.filter.keywords && this.collection.state !== 'error' && this.collection.length !== 0) { - $('.items').append('
' + i18n.__('Load More') + '
'); - - $('#load-more-item').click(function () { + $('#load-more-item').css('display', 'inline-block').click(function () { $('#load-more-item').off('click'); self.collection.fetchMore(); }); diff --git a/src/app/lib/views/main_window.js b/src/app/lib/views/main_window.js index d09b6d8f12..3774545073 100644 --- a/src/app/lib/views/main_window.js +++ b/src/app/lib/views/main_window.js @@ -511,6 +511,10 @@ '.item {', 'font-size: ' + fontSize + 'em;', + '}', + + '.ghost {', + 'width: ', postersWidth, 'px;', '}' ].join(''); diff --git a/src/app/lib/views/player/player.js b/src/app/lib/views/player/player.js index c92ed36233..4877286b76 100644 --- a/src/app/lib/views/player/player.js +++ b/src/app/lib/views/player/player.js @@ -36,12 +36,14 @@ this.listenTo(this.model, 'change:active_peers', this.updateActivePeers); this.listenTo(this.model, 'change:downloaded', this.updateDownloaded); - this.inFullscreen = win.isFullscreen; - this.playerWasReady = false; + this.inFullscreen = win.isFullscreen; + this.playerWasReady = false; - this.remaining = false; - this.createdRemaining = false; + this.remaining = false; + this.createdRemaining = false; this.firstPlay = true; + + this.boundedMouseScroll = this.mouseScroll.bind(this); }, isMovie: function () { @@ -403,7 +405,7 @@ customSubtitles: {}, progressTips: {} } - }).ready(function () { + }).ready(function () { that.playerWasReady = Date.now(); }); } @@ -414,7 +416,8 @@ this.player.click = 0; this.player.tech.off('mousedown'); // stop listening to default ev this.player.tech.on('mouseup', this.onClick.bind(this)); - $('#video_player').dblclick(this.onDbClick.bind(this)); + this.player.tech.on('touchend', this.onClick.bind(this)); // touchscreen fix + this.player.tech.on('dblclick', this.onDbClick.bind(this)); // Force custom controls this.player.usingNativeControls(false); @@ -778,7 +781,7 @@ } }); - $('body').bind('mousewheel', this.mouseScroll.bind(this)); + document.addEventListener('mousewheel', this.boundedMouseScroll); }, unbindKeyboardShortcuts: function () { @@ -849,7 +852,7 @@ // Change when mousetrap can be extended $('body').unbind('keydown'); - $('body').unbind('mousewheel'); + document.removeEventListener('mousewheel', this.boundedMouseScroll); }, toggleMouseDebug: function () { @@ -952,7 +955,7 @@ }, onDestroy: function () { - if (this.model.get('type') === 'video/youtube') { + if (this.model.get('type') === 'video/youtube') { $('.trailer_mouse_catch').remove(); // Trailer UI Show FIX/HACK } $('#player_drag').hide(); diff --git a/src/app/lib/views/title_bar.js b/src/app/lib/views/title_bar.js index 178693ec2e..a232d4ac53 100644 --- a/src/app/lib/views/title_bar.js +++ b/src/app/lib/views/title_bar.js @@ -58,14 +58,8 @@ } else { if (window.screen.availHeight <= this.nativeWindow.height) { this.nativeWindow.restore(); - if (process.platform === 'win32') { - $('.os-max').removeClass('os-is-max'); - } } else { this.nativeWindow.maximize(); - if (process.platform === 'win32') { - $('.os-max').addClass('os-is-max'); - } } } }, @@ -95,6 +89,10 @@ 'hide': 100 } }); + + if (Boolean(parseInt(localStorage.isMaximized))) { + $('.os-max').addClass('os-is-max'); + } } }); diff --git a/src/app/styl/views/about.styl b/src/app/styl/views/about.styl index a5c010e3c5..4f19cf8973 100644 --- a/src/app/styl/views/about.styl +++ b/src/app/styl/views/about.styl @@ -9,7 +9,7 @@ background-image url('../images/bg-header.jpg') background-size cover background-position 50% 50% - + .margintop height calc(20% - 20px) @@ -60,7 +60,7 @@ margin 0 auto max-width 42em font-size 1em - + .full-text height 90px @@ -90,19 +90,23 @@ a.twitter_icon background url(../images/icons/icon-twitter.png) no-repeat center background-size contain - + a.facebook_icon background url(../images/icons/icon-facebook.png) no-repeat center background-size contain - + a.google_icon background url(../images/icons/icon-google.png) no-repeat center background-size contain - + a.stash_icon background url(../images/icons/icon-stash.png) no-repeat center background-size contain + a.github_icon + background url(../images/icons/icon-github.png) no-repeat center + background-size contain + a.gitlab_icon background url(../images/icons/icon-gitlab.png) no-repeat center background-size contain @@ -110,7 +114,7 @@ a.blog_icon background url(../images/icons/icon-blog.png) no-repeat center background-size contain - + a.forum_icon background url(../images/icons/icon-discourse.png) no-repeat center background-size contain @@ -150,4 +154,4 @@ margin 0 auto margin-top 2vh padding 0px 15px - scrollable() \ No newline at end of file + scrollable() diff --git a/src/app/styl/views/browser/item.styl b/src/app/styl/views/browser/item.styl index 52cd0403a0..64da6c6dad 100644 --- a/src/app/styl/views/browser/item.styl +++ b/src/app/styl/views/browser/item.styl @@ -20,11 +20,15 @@ border-radius: $PosterRadius box-shadow: $PosterShadow cursor: pointer + + &.empty + background-image none .cover-overlay - text-align center width 100% height 100% + background-size cover + background-position center position absolute transition opacity 500ms ease-in opacity 0 @@ -163,7 +167,7 @@ border-radius 4px box-shadow $PosterShadow cursor pointer - display inline-block + display none margin 10px float left background-color:$BgColor1 @@ -207,3 +211,8 @@ background-color:$ButtonBgHover &:active background-color:$ButtonBgActive + +.ghost + width 134px + height 0 + margin 10px \ No newline at end of file diff --git a/src/app/styl/views/browser/list.styl b/src/app/styl/views/browser/list.styl index 2c6461f529..7b470708c4 100644 --- a/src/app/styl/views/browser/list.styl +++ b/src/app/styl/views/browser/list.styl @@ -73,7 +73,6 @@ justify-content: space-between align-content: flex-start align-items: flex-start - justify-content: flex-start width: calc(100% - 5px) .error diff --git a/src/app/styl/views/title_bar.styl b/src/app/styl/views/title_bar.styl index b810ada11a..e0d15dcd02 100644 --- a/src/app/styl/views/title_bar.styl +++ b/src/app/styl/views/title_bar.styl @@ -106,44 +106,43 @@ .btn-set.win32 background-size: auto auto - top: 6px - right: 3px + top: 1px + right: 1px .btn-os float: left - margin: 0 2px 0 0 - background: #000 + margin: 0 + background-color: transparent -webkit-border-radius: 0px border-radius: 0px border: 0 color: transparent display: inline-block font-size: 10px - height: 16px + height: 30px padding: 0 text-align: center - width: 20px + width: 45px cursor: pointer - background: url("../images/icons/topbar_sprite_win.png") + background-image: url("../images/icons/topbar_sprite_win.png") + background-size: cover -webkit-filter $PngLogo &.os-close - background-position -40px 0px + background-position -92px 0px &.os-max - background-position -20px 0px + background-position -46px 0px &.os-is-max - background-position -120px 0px + background-position 92px 0px &.os-min background-position 0px 0px &.os-close:hover - background-position -100px 0px + background-position 138px 0px -webkit-filter brightness(1) &.os-max:hover - background-position -80px 0px - -webkit-filter brightness(1) + background-position 184px 0px &.os-is-max:hover - background-position -140px 0px + background-position 46px 0px &.os-min:hover - background-position -60px 0px - -webkit-filter brightness(1) + background-position -138px 0px .btn-set.darwin diff --git a/src/app/templates/about.tpl b/src/app/templates/about.tpl index 4ebb2ea436..160717fdd9 100644 --- a/src/app/templates/about.tpl +++ b/src/app/templates/about.tpl @@ -1,3 +1,11 @@ +<% +if(Settings.sourceUrl.match('github.com')){ + source_icon = 'github_icon' +} else { + source_icon = 'gitlab_icon' +}; +%> +
@@ -24,7 +32,7 @@ - +
@@ -36,6 +44,6 @@
<%=i18n.__("Changelog")%>
-
+