diff options
-rw-r--r-- | .SRCINFO | 8 | ||||
-rw-r--r-- | PKGBUILD | 9 | ||||
-rw-r--r-- | package.json | 110 | ||||
-rw-r--r-- | torrent_collection.js | 427 | ||||
-rw-r--r-- | ysubs.js | 146 |
5 files changed, 691 insertions, 9 deletions
@@ -1,6 +1,6 @@ pkgbase = popcorntime-git pkgdesc = Stream movies and TV shows from torrents - pkgver = r5909.49c524e + pkgver = r5929.1815690 pkgrel = 1 url = https://popcorntime.sh install = popcorntime.install @@ -25,9 +25,9 @@ pkgbase = popcorntime-git conflicts = popcorn-time-ce options = !strip source = git+https://github.com/popcorn-official/popcorn-desktop.git#branch=master - source = https://popcorntime.sh/package.json - source = https://popcorntime.sh/torrent_collection.js - source = https://popcorntime.sh/ysubs.js + source = package.json + source = torrent_collection.js + source = ysubs.js source = popcorntime.desktop sha256sums = SKIP sha256sums = cb1eea1c8e40b8d65014ffe126039221da813aa5bdfc838b998513bdce47cb0c @@ -5,7 +5,7 @@ pkgname=popcorntime-git _pkgname=popcorntime _gitname=popcorn-desktop -pkgver=r5909.49c524e +pkgver=r5929.1815690 pkgrel=1 pkgdesc="Stream movies and TV shows from torrents" arch=('i686' 'x86_64') @@ -18,9 +18,9 @@ provides=('popcorntime') options=('!strip') install=popcorntime.install source=("git+https://github.com/popcorn-official/${_gitname}.git#branch=master" - "https://popcorntime.sh/package.json" - "https://popcorntime.sh/torrent_collection.js" - "https://popcorntime.sh/ysubs.js" + "package.json" + "torrent_collection.js" + "ysubs.js" "${_pkgname}.desktop") sha256sums=('SKIP' 'cb1eea1c8e40b8d65014ffe126039221da813aa5bdfc838b998513bdce47cb0c' @@ -74,4 +74,3 @@ package() { } # vim:set ts=2 sw=2 et: - diff --git a/package.json b/package.json new file mode 100644 index 000000000000..f6f36da3841b --- /dev/null +++ b/package.json @@ -0,0 +1,110 @@ +{ + "name": "Popcorn-Time", + "companyName": "Popcorn Time", + "installIcon": "./src/app/images/popcorntime.ico", + "unInstallIcon": "./src/app/images/butter_uninstall.ico", + "homepage": "http://popcorntime.sh/", + "bugs": "https://github.com/popcorn-official/popcorn-desktop/issues", + "repository": { + "type": "git", + "url": "https://github.com/popcorn-official/popcorn-desktop.git" + }, + "license": "GPL-3.0", + "main": "app://host/src/app/index.html", + "version": "0.3.9", + "releaseName": "There's nothing on TV", + "scripts": { + "postinstall": "node_modules/.bin/bower install --config.interactive=false && grunt setup", + "postupdate": "node_modules/.bin/bower update --config.interactive=false && grunt setup", + "test": "grunt --verbose", + "start": "gulp start" + }, + "window": { + "title": "Popcorn Time", + "icon": "./src/app/images/icon.png", + "toolbar": false, + "frame": false, + "min_width": 960, + "min_height": 520, + "resizable": true, + "show": false, + "position": "center" + }, + "dependencies": { + "butter-provider": "git+https://github.com/butterproviders/butter-provider", + "butter-settings-popcorntime.io": "git+https://github.com/popcorn-official/butter-settings-popcorn", + "butter-provider-yts": "git+https://github.com/team-pct/butter-provider-yts", + "butter-provider-tvapi": "git+https://github.com/team-pct/butter-provider-tvapi", + "butter-provider-haruhichan": "git+https://github.com/hackhackhack/butter-provider-haruhichan", + "butter-provider-vodo": "git+https://github.com/butterproviders/butter-provider-vodo", + "popcorn-txt-api": "git+https://github.com/hackhackhack/popcorn-txt-api", + + "adm-zip": "0.4.7", + "airplay-js": "git+https://github.com/guerrerocarlos/node-airplay-js.git", + "async": "1.5.0", + "chromecast-js": "git+https://github.com/alxhotel/chromecast-js.git", + "kat-api": "git+https://github.com/team-pct/kat-api", + "rarbg-api": "git+https://github.com/team-pct/rarbg-api", + "defer-request": "0.0.2", + "i18n": "0.x.x", + "iconv-lite": "0.x.x", + "jschardet": "1.4.1", + "json-rpc2": "1.0.2", + "markdown": "0.5.x", + "memoizee": "0.x.x", + "mkdirp": "*", + "moment": ">=2.11.2", + "mv": "2.x.x", + "nedb": "1.4.0", + "node-captions": "0.4.6", + "node-tvdb": "1.6.x", + "node-webkit-fdialogs": "latest", + "nodecast-js": "^1.0.1", + "opensubtitles-api": "^2.3.x", + "os-name": "1.x.x", + "peerflix": "git+https://github.com/popcorn-official/peerflix.git", + "q": "2.0.3", + "read-torrent": "1.3.0", + "readdirp": "*", + "request": "2.x.x", + "rimraf": "2.x.x", + "sanitizer": "0.x.x", + "semver": "5.x.x", + "send": "0.13.x", + "strike-api": "0.2.0", + "tar": "2.2.1", + "temp": "0.x.x", + "torrent-tracker-health": "0.x.x", + "trakt.tv": "1.x.x", + "trakt.tv-ondeck": "0.x", + "underscore": "1.x.x", + "upnp-mediarenderer-client": "1.2.2", + "urijs": "1.x.x", + "xmlbuilder": "4.1.0" + }, + "devDependencies": { + "bower": "1.7.x", + "del": "^2.2.0", + "grunt": "0.4.5", + "grunt-bower-clean": "0.x.x", + "grunt-contrib-clean": "1.x.x", + "grunt-contrib-compress": "1.x.x", + "grunt-contrib-jshint": "1.x.x", + "grunt-contrib-stylus": "1.x.x", + "grunt-contrib-watch": "0.6.x", + "grunt-exec": "0.4.x", + "grunt-githooks": "0.x.x", + "grunt-jsbeautifier": "*", + "grunt-nw-builder": "^2.0.0", + "grunt-shell": "1.x.x", + "gulp": "^3.9.0", + "gulp-stylus": "^2.3.0", + "load-grunt-tasks": "3.x.x", + "nib": "^1.1.0", + "nw-builder": "^2.0.2", + "nw-gyp": "0.x.x", + "run-sequence": "^1.1.5", + "stylus": "0.x.x", + "yargs": "^3.31.0" + } +} diff --git a/torrent_collection.js b/torrent_collection.js new file mode 100644 index 000000000000..89d33492db9b --- /dev/null +++ b/torrent_collection.js @@ -0,0 +1,427 @@ +(function (App) { + 'use strict'; + + var clipboard = gui.Clipboard.get(), + collection = path.join(data_path + '/TorrentCollection/'); + + var TorrentCollection = Backbone.Marionette.ItemView.extend({ + template: '#torrent-collection-tpl', + className: 'torrent-collection', + + events: { + 'mousedown .file-item': 'openFileSelector', + 'mousedown .result-item': 'onlineOpen', + 'mousedown .item-delete': 'deleteItem', + 'mousedown .item-rename': 'renameItem', + 'mousedown .magnet-icon': 'openMagnet', + 'click .collection-delete': 'clearCollection', + 'click .collection-open': 'openCollection', + 'click .collection-import': 'importItem', + 'click .notorrents-frame': 'importItem', + 'click .online-search': 'onlineSearch', + 'click .engine-icon': 'changeEngine', + 'submit #online-form': 'onlineSearch', + 'click .online-back': 'onlineClose', + 'contextmenu #online-input': 'rightclick_search' + }, + + initialize: function () { + if (!fs.existsSync(collection)) { + fs.mkdirSync(collection); + console.debug('TorrentCollection: data directory created'); + } + this.files = fs.readdirSync(collection); + this.searchEngine = Settings.onlineSearchEngine; + }, + + onShow: function () { + Mousetrap.bind(['esc', 'backspace'], function (e) { + $('#filterbar-torrent-collection').click(); + }); + + this.render(); + }, + + onRender: function () { + $('.engine-icon').removeClass('active'); + $('#' + this.searchEngine.toLowerCase() + '-icon').addClass('active'); + $('#online-input').focus(); + if (this.files[0]) { + $('.notorrents-info').css('display', 'none'); + $('.collection-actions').css('display', 'block'); + $('.torrents-info').css('display', 'block'); + } + + this.$('.tooltipped').tooltip({ + delay: { + 'show': 800, + 'hide': 100 + } + }); + }, + + changeEngine: function (e) { + e.preventDefault(); + + Settings.onlineSearchEngine = this.searchEngine = e.currentTarget.dataset.id; + AdvSettings.set('onlineSearchEngine', this.searchEngine); + + if ($('#online-input').val().length !== 0) { + $('.engine-icon').removeClass('active'); + $('#' + this.searchEngine.toLowerCase() + '-icon').addClass('active'); + this.onlineSearch(); + } else { + this.render(); + } + }, + + onlineSearch: function (e) { + if (e) { + e.preventDefault(); + } + var that = this; + var input = $('#online-input').val(); + var category = $('.online-categories > select').val(); + AdvSettings.set('OnlineSearchCategory', category); + if (category === 'TV Series') { + category = 'TV'; + } + var current = $('.onlinesearch-info > ul.file-list').html(); + + if (input === '' && current === '') { + return; + } else if (input === '' && current !== '') { + this.onlineClose(); + return; + } + + $('.onlinesearch-info>ul.file-list').html(''); + + $('.online-search').removeClass('fa-search').addClass('fa-spin fa-spinner'); + + var index = 0; + + if (this.searchEngine === 'KAT') { + + var kat = require('kat-api'); + kat.search({ + query: input.toLocaleLowerCase(), + category: category.toLocaleLowerCase() //IT IS BUGGED ATM + }).then(function (data) { + console.debug('KAT search: %s results', data.results.length); + data.results.forEach(function (item) { + var itemModel = { + title: item.title, + magnet: item.magnet, + seeds: item.seeds, + peers: item.peers, + size: Common.fileSize(parseInt(item.size)), + index: index + }; + + if (item.title.match(/trailer/i) !== null && input.match(/trailer/i) === null) { + return; + } + that.onlineAddItem(itemModel); + index++; + }); + + that.$('.tooltipped').tooltip({ + html: true, + delay: { + 'show': 50, + 'hide': 50 + } + }); + $('.notorrents-info,.torrents-info').hide(); + $('.online-search').removeClass('fa-spin fa-spinner').addClass('fa-search'); + $('.onlinesearch-info').show(); + if (index === 0) { + $('.onlinesearch-info>ul.file-list').html('<br><br><div style="text-align:center;font-size:30px">' + i18n.__('No results found') + '</div>'); + } + }).catch(function (err) { + console.debug('KAT search failed:', err.message); + var error; + if (err.message === 'No results') { + error = 'No results found'; + } else { + error = 'Failed!'; + } + $('.onlinesearch-info>ul.file-list').html('<br><br><div style="text-align:center;font-size:30px">' + i18n.__(error) + '</div>'); + + $('.online-search').removeClass('fa-spin fa-spinner').addClass('fa-search'); + $('.notorrents-info,.torrents-info').hide(); + $('.onlinesearch-info').show(); + }); + + } else { + + var rarbg = require('rarbg-api'); + rarbg.search(input, category).then(function (result) { + console.debug('rarbg search: %s results', result.results.length); + result.results.forEach(function (item) { + var itemModel = { + title: item.title, + magnet: item.torrentLink, + seeds: item.seeds, + peers: item.leechs, + size: Common.fileSize(parseInt(item.size)), + index: index + }; + + if (item.title.match(/trailer/i) !== null && input.match(/trailer/i) === null) { + return; + } + that.onlineAddItem(itemModel); + index++; + }); + + that.$('.tooltipped').tooltip({ + html: true, + delay: { + 'show': 50, + 'hide': 50 + } + }); + $('.notorrents-info,.torrents-info').hide(); + $('.online-search').removeClass('fa-spin fa-spinner').addClass('fa-search'); + $('.onlinesearch-info').show(); + if (index === 0) { + $('.onlinesearch-info>ul.file-list').html('<br><br><div style="text-align:center;font-size:30px">' + i18n.__('No results found') + '</div>'); + } + }).catch(function (err) { + console.debug('rarbg search failed:', err.message || err); + var error; + if (err === 'No torrents found') { + error = 'No results found'; + } else if (err && err.match(/bot/i) !== null) { + error = 'RARBG thinks you\'re a bot, check <a class="links" href="https://www.rarbg.com/bot_check.php">https://www.rarbg.com/bot_check.php</a>'; + } else if (err === 'There was a problem loading Rarbg') { + error = 'RARBG could not be contacted<br>Please retry or check <a class="links" href="https://www.rarbg.com/">https://rarbg.com/</a>'; + } else { + error = 'Failed!'; + } + $('.onlinesearch-info>ul.file-list').html('<br><br><div style="text-align:center;font-size:30px">' + i18n.__(error) + '</div>'); + + $('.online-search').removeClass('fa-spin fa-spinner').addClass('fa-search'); + $('.notorrents-info,.torrents-info').hide(); + $('.onlinesearch-info').show(); + }); + } + }, + + onlineAddItem: function (item) { + var ratio = item.peers > 0 ? item.seeds / item.peers : +item.seeds; + $('.onlinesearch-info>ul.file-list').append( + '<li class="result-item" data-index="' + item.index + '" data-file="' + item.magnet + '"><a>' + item.title + '</a><div class="item-icon magnet-icon tooltipped" data-toogle="tooltip" data-placement="right" title="' + i18n.__('Magnet link') + '"></div><i class="online-size tooltipped" data-toggle="tooltip" data-placement="left" title="' + i18n.__('Ratio:') + ' ' + ratio.toFixed(2) + '<br>' + i18n.__('Seeds:') + ' ' + item.seeds + ' - ' + i18n.__('Peers:') + ' ' + item.peers + '">' + item.size + '</i></li>' + ); + if (item.seeds === 0) { // recalc the peers/seeds + var torrent = item.magnet.split('&tr')[0] + '&tr=udp://tracker.openbittorrent.com:80/announce' + '&tr=udp://9.rarbg.com:2710/announce' + '&tr=udp://tracker.coppersurfer.tk:6969' + '&tr=udp://tracker.publicbt.com:80/announce'; + require('torrent-tracker-health')(torrent, { + timeout: 1000 + }).then(function (res) { + //console.log('torrent index %s: %s -> %s (seeds)', item.index, item.seeds, res.seeds) + ratio = res.peers > 0 ? res.seeds / res.peers : +res.seeds; + $('.result-item[data-index=' + item.index + '] i').attr('data-original-title', i18n.__('Ratio:') + ' ' + ratio.toFixed(2) + '<br>' + i18n.__('Seeds:') + ' ' + res.seeds + ' - ' + i18n.__('Peers:') + ' ' + res.peers); + }); + } + }, + + onlineOpen: function (e) { + var file = $(e.currentTarget).context.dataset.file; + Settings.droppedMagnet = file; + window.handleTorrent(file); + }, + + onlineClose: function () { + $('.onlinesearch-info>ul.file-list').html(''); + $('.onlinesearch-info').hide(); + this.render(); + }, + + rightclick_search: function (e) { + e.stopPropagation(); + var search_menu = new this.context_Menu(i18n.__('Cut'), i18n.__('Copy'), i18n.__('Paste')); + search_menu.popup(e.originalEvent.x, e.originalEvent.y); + }, + + context_Menu: function (cutLabel, copyLabel, pasteLabel) { + var menu = new gui.Menu(), + + cut = new gui.MenuItem({ + label: cutLabel || 'Cut', + click: function () { + document.execCommand('cut'); + } + }), + + copy = new gui.MenuItem({ + label: copyLabel || 'Copy', + click: function () { + document.execCommand('copy'); + } + }), + + paste = new gui.MenuItem({ + label: pasteLabel || 'Paste', + click: function () { + var text = clipboard.get('text'); + $('#online-input').val(text); + } + }); + + menu.append(cut); + menu.append(copy); + menu.append(paste); + + return menu; + }, + + openFileSelector: function (e) { + var _file = $(e.currentTarget).context.innerText, + file = _file.substring(0, _file.length - 2); // avoid ENOENT + + if (_file.indexOf('.torrent') !== -1) { + Settings.droppedTorrent = file; + window.handleTorrent(collection + file); + } else { // assume magnet + var content = fs.readFileSync(collection + file, 'utf8'); + Settings.droppedMagnet = content; + Settings.droppedStoredMagnet = file; + window.handleTorrent(content); + } + }, + + openMagnet: function (e) { + this.$('.tooltip').css('display', 'none'); + e.preventDefault(); + e.stopPropagation(); + + var magnetLink; + + if ($(e.currentTarget.parentNode).context.className === 'file-item') { + // stored + var _file = $(e.currentTarget.parentNode).context.innerText, + file = _file.substring(0, _file.length - 2); // avoid ENOENT + magnetLink = fs.readFileSync(collection + file, 'utf8'); + } else { + // search result + magnetLink = $(e.currentTarget.parentNode).context.attributes['data-file'].value; + } + + if (e.button === 2) { //if right click on magnet link + var clipboard = gui.Clipboard.get(); + clipboard.set(magnetLink, 'text'); //copy link to clipboard + $('.notification_alert').text(i18n.__('The magnet link was copied to the clipboard')).fadeIn('fast').delay(2500).fadeOut('fast'); + } else { + gui.Shell.openExternal(magnetLink); + } + }, + + deleteItem: function (e) { + this.$('.tooltip').css('display', 'none'); + e.preventDefault(); + e.stopPropagation(); + + var _file = $(e.currentTarget.parentNode).context.innerText, + file = _file.substring(0, _file.length - 2); // avoid ENOENT + + fs.unlinkSync(collection + file); + console.debug('Torrent Collection: deleted', file); + + // update collection + this.files = fs.readdirSync(collection); + this.render(); + }, + + renameItem: function (e) { + this.$('.tooltip').css('display', 'none'); + e.preventDefault(); + e.stopPropagation(); + + var _file = $(e.currentTarget.parentNode).context.innerText, + file = _file.substring(0, _file.length - 2), // avoid ENOENT + isTorrent = false; + + if (file.endsWith('.torrent')) { + isTorrent = 'torrent'; + } + + var newName = this.renameInput(file); + if (!newName) { + return; + } + + if (isTorrent) { //torrent + if (!newName.endsWith('.torrent')) { + newName += '.torrent'; + } + } else { //magnet + if (newName.endsWith('.torrent')) { + newName = newName.replace('.torrent', ''); + } + } + + if (!fs.existsSync(collection + newName) && newName) { + fs.renameSync(collection + file, collection + newName); + console.debug('Torrent Collection: renamed', file, 'to', newName); + } else { + $('.notification_alert').show().text(i18n.__('This name is already taken')).delay(2500).fadeOut(400); + } + + // update collection + this.files = fs.readdirSync(collection); + this.render(); + }, + + renameInput: function (oldName) { + var userInput = prompt(i18n.__('Enter new name'), oldName); + if (!userInput || userInput === oldName) { + return false; + } else { + return userInput; + } + }, + + clearCollection: function () { + deleteFolder(collection); + console.debug('Torrent Collection: delete all', collection); + App.vent.trigger('torrentCollection:show'); + }, + + openCollection: function () { + console.debug('Opening: ' + collection); + gui.Shell.openItem(collection); + }, + + importItem: function () { + this.$('.tooltip').css('display', 'none'); + + var that = this; + var input = document.querySelector('.collection-import-hidden'); + input.addEventListener('change', function (evt) { + var file = $('.collection-import-hidden')[0].files[0]; + that.render(); + window.ondrop({ + dataTransfer: { + files: [file] + }, + preventDefault: function () {} + }); + }, false); + + input.click(); + }, + + onDestroy: function () { + Mousetrap.unbind(['esc', 'backspace']); + }, + + closeTorrentCollection: function () { + App.vent.trigger('torrentCollection:close'); + } + + }); + + App.View.TorrentCollection = TorrentCollection; +})(window.App); diff --git a/ysubs.js b/ysubs.js new file mode 100644 index 000000000000..a1c0db04d84f --- /dev/null +++ b/ysubs.js @@ -0,0 +1,146 @@ +(function (context) { + 'use strict'; + + var App = context.App ? context.App : context; + var _ = require('underscore'); + var request = require('request'); + var Q = require('q'); + + var baseUrl = 'http://api.yifysubtitles.com/subs/'; + var mirrorUrl = 'http://api.ysubs.com/subs/'; + var prefix = 'http://www.yifysubtitles.com'; + + var inherits = require('util').inherits; + + var TTL = 1000 * 60 * 60 * 4; // 4 hours + var YSubs = function () { + if (!(this instanceof YSubs)) { + return new YSubs(); + } + + App.Providers.CacheProvider.call(this, 'subtitle', TTL); + }; + + // Language mapping to match PT langcodes + var languageMapping = { + 'albanian': 'sq', + 'arabic': 'ar', + 'bengali': 'bn', + 'brazilian-portuguese': 'pt-br', + 'bulgarian': 'bg', + 'bosnian': 'bs', + 'chinese': 'zh', + 'croatian': 'hr', + 'czech': 'cs', + 'danish': 'da', + 'dutch': 'nl', + 'english': 'en', + 'estonian': 'et', + 'farsi-persian': 'fa', + 'finnish': 'fi', + 'french': 'fr', + 'german': 'de', + 'greek': 'el', + 'hebrew': 'he', + 'hungarian': 'hu', + 'indonesian': 'id', + 'italian': 'it', + 'japanese': 'ja', + 'korean': 'ko', + 'lithuanian': 'lt', + 'macedonian': 'mk', + 'malay': 'ms', + 'norwegian': 'no', + 'polish': 'pl', + 'portuguese': 'pt', + 'romanian': 'ro', + 'russian': 'ru', + 'serbian': 'sr', + 'slovenian': 'sl', + 'spanish': 'es', + 'swedish': 'sv', + 'thai': 'th', + 'turkish': 'tr', + 'urdu': 'ur', + 'ukrainian': 'uk', + 'vietnamese': 'vi' + }; + + + + YSubs.prototype = Object.create(App.Providers.CacheProvider.prototype); + YSubs.prototype.constructor = YSubs; + YSubs.prototype.config = { + name: 'ysubs', + type: 'subtitle' + }; + + var querySubtitles = function (imdbIds) { + if (_.isEmpty(imdbIds)) { + return {}; + } + + var url = baseUrl + _.map(imdbIds.sort(), function (id) { + return id; + }).join('-'); + var mirrorurl = mirrorUrl + _.map(imdbIds.sort(), function (id) { + return id; + }).join('-'); + + var deferred = Q.defer(); + + request({ + url: url, + json: true + }, function (error, response, data) { + if (error || response.statusCode >= 400 || !data || !data.success) { + request({ + url: mirrorurl, + json: true + }, function (error, response, data) { + if (error || response.statusCode >= 400 || !data || !data.success) { + deferred.reject(error); + } else { + deferred.resolve(data); + } + }); + } else { + deferred.resolve(data); + } + }); + + return deferred.promise; + }; + + var formatForPopcorn = function (data) { + var allSubs = {}; + // Iterate each movie + _.each(data.subs, function (langs, imdbId) { + var movieSubs = {}; + // Iterate each language + _.each(langs, function (subs, lang) { + // Pick highest rated + var langCode = languageMapping[lang]; + movieSubs[langCode] = prefix + _.max(subs, function (s) { + return s.rating; + }).url; + }); + + // Remove unsupported subtitles + var filteredSubtitle = App.Localization.filterSubtitle(movieSubs); + + allSubs[imdbId] = filteredSubtitle; + }); + + return Common.sanitize(allSubs); + }; + + YSubs.prototype.query = function (ids) { + return Q.when(querySubtitles(ids)) + .then(formatForPopcorn); + }; + + + + App.Providers.install(YSubs); +})(window); |