diff --git a/config/env/torrents.js b/config/env/torrents.js index 72b71236..1511c46f 100644 --- a/config/env/torrents.js +++ b/config/env/torrents.js @@ -664,9 +664,11 @@ module.exports = { AdminTorrentSetSaleType: {name: 'AdminTorrentSetSaleType', enable: true}, AdminTorrentSetRecommendLevel: {name: 'AdminTorrentSetRecommendLevel', enable: true}, AdminCollectionSetRecommendLevel: {name: 'AdminCollectionSetRecommendLevel', enable: true}, + AdminAlbumSetRecommendLevel: {name: 'AdminAlbumSetRecommendLevel', enable: true}, AdminTorrentSetReviewedStatus: {name: 'AdminTorrentSetReviewedStatus', enable: true}, OperCreateMakerGroup: {name: 'OperCreateMakerGroup', enable: true}, OperCreateCollection: {name: 'OperCreateCollection', enable: true}, + OperCreateAlbum: {name: 'OperCreateAlbum', enable: true}, OperDeleteBackupFiles: {name: 'OperDeleteBackupFiles', enable: true}, userInvitationExchange: {name: 'userInvitationExchange', enable: true}, diff --git a/modules/albums/client/albums.client.module.js b/modules/albums/client/albums.client.module.js new file mode 100644 index 00000000..c5209f53 --- /dev/null +++ b/modules/albums/client/albums.client.module.js @@ -0,0 +1,7 @@ +(function (app) { + 'use strict'; + + app.registerModule('albums', ['core']);// The core module is required for special route handling; see /core/client/config/core.client.routes + app.registerModule('albums.services'); + app.registerModule('albums.routes', ['ui.router', 'core.routes', 'albums.services']); +}(ApplicationConfiguration)); diff --git a/modules/albums/client/config/albums.client.routes.js b/modules/albums/client/config/albums.client.routes.js new file mode 100644 index 00000000..e28ff689 --- /dev/null +++ b/modules/albums/client/config/albums.client.routes.js @@ -0,0 +1,37 @@ +(function () { + 'use strict'; + + angular + .module('albums.routes') + .config(routeConfig); + + routeConfig.$inject = ['$stateProvider']; + + function routeConfig($stateProvider) { + + $stateProvider + // this menu item defined in torrents menu config + .state('albums', { + url: '/albums', + abstract: true, + template: '', + data: { + roles: ['user', 'oper', 'admin'] + } + }) + .state('albums.list', { + url: '', + templateUrl: '/modules/albums/client/views/albums.client.view.html', + data: { + pageTitle: 'PAGETITLE.ALBUMS' + } + }) + .state('albums.view', { + url: '/:collectionId', + templateUrl: '/modules/albums/client/views/albums-view.client.view.html', + data: { + pageTitle: 'PAGETITLE.ALBUMS' + } + }); + } +}()); diff --git a/modules/albums/client/controllers/album-view.client.controller.js b/modules/albums/client/controllers/album-view.client.controller.js new file mode 100644 index 00000000..50576c3b --- /dev/null +++ b/modules/albums/client/controllers/album-view.client.controller.js @@ -0,0 +1,238 @@ +(function () { + 'use strict'; + + angular + .module('albums') + .controller('AlbumItemController', AlbumItemController); + + AlbumItemController.$inject = ['$scope', '$state', '$translate', 'MeanTorrentConfig', 'CollectionsService', 'NotifycationService', 'DownloadService', + 'DebugConsoleService', 'TorrentGetInfoServices', 'Authentication', 'ResourcesTagsServices', 'ModalConfirmService', 'localStorageService', + '$compile', 'marked', '$window']; + + function AlbumItemController($scope, $state, $translate, MeanTorrentConfig, CollectionsService, NotifycationService, DownloadService, + mtDebug, TorrentGetInfoServices, Authentication, ResourcesTagsServices, ModalConfirmService, localStorageService, + $compile, marked, $window) { + var vm = this; + vm.DLS = DownloadService; + vm.TGI = TorrentGetInfoServices; + vm.user = Authentication.user; + vm.RTS = ResourcesTagsServices; + vm.tmdbConfig = MeanTorrentConfig.meanTorrentConfig.tmdbConfig; + vm.torrentRLevels = MeanTorrentConfig.meanTorrentConfig.torrentRecommendLevel; + vm.inputLengthConfig = MeanTorrentConfig.meanTorrentConfig.inputLength; + + vm.searchTags = []; + vm.release = []; + + /** + * getCollection + */ + vm.getCollection = function () { + CollectionsService.get({ + collectionId: $state.params.collectionId + }, function (data) { + vm.collection = data; + + $('.backdrop').css('backgroundImage', 'url("' + vm.tmdbConfig.backdropImgBaseUrl + vm.collection.backdrop_path + '")'); + + //count ave vote + var total = 0; + var count = 0; + var total_users = 0; + angular.forEach(vm.collection.torrents, function (t) { + total += t.resource_detail_info.vote_average; + count += 1; + total_users += t.resource_detail_info.vote_count; + + vm.release.push(parseInt(t.resource_detail_info.release_date, 10)); + }); + + vm.collection.vote_count = total_users; + vm.collection.vote_average = Math.floor((total / count) * 10) / 10; + + mtDebug.info(vm.collection); + mtDebug.info(vm.release); + }); + + }; + + /** + * getMinMaxRelease + * @param c + * @returns {{min: *, max: *}} + */ + vm.getMinMaxRelease = function () { + return { + min: vm.release.length > 0 ? Math.min.apply(null, vm.release) : '', + max: vm.release.length > 0 ? Math.max.apply(null, vm.release) : '' + }; + }; + + /** + * getCollectionOverviewContent + * @param m + * @returns {*} + */ + vm.getCollectionOverviewContent = function (c) { + return c ? marked(c.overview, {sanitize: true}) : ''; + }; + + /** + * beginRemoveCollection + * @param m + */ + vm.beginRemoveCollection = function (c) { + var modalOptions = { + closeButtonText: $translate.instant('ABOUT.DELETE_CONFIRM_CANCEL'), + actionButtonText: $translate.instant('ABOUT.DELETE_CONFIRM_OK'), + headerText: $translate.instant('ABOUT.DELETE_CONFIRM_HEADER_TEXT'), + bodyText: $translate.instant('COLLECTIONS.DELETE_CONFIRM_BODY_TEXT') + }; + + ModalConfirmService.showModal({}, modalOptions) + .then(function (result) { + c.$remove(function (res) { + NotifycationService.showSuccessNotify('COLLECTIONS.DELETE_SUCCESSFULLY'); + $state.go('collections.list'); + }, function (res) { + NotifycationService.showErrorNotify(res.data.message, 'COLLECTIONS.DELETE_FAILED'); + }); + }); + }; + + /** + * beginEditCollectionOverview + * @param m + */ + vm.beginEditCollectionOverview = function (c) { + var el = $('#' + c._id); + + el.markdown({ + autofocus: true, + savable: true, + hideable: true, + iconlibrary: 'fa', + resize: 'vertical', + language: localStorageService.get('storage_user_lang'), + fullscreen: {enable: false}, + onSave: function (e) { + if (e.isDirty()) { + vm.collection.overview = e.getContent(); + vm.collection.$update(function (res) { + vm.collection = res; + NotifycationService.showSuccessNotify('COLLECTIONS.EDIT_OVERVIEW_SUCCESSFULLY'); + }, function (res) { + NotifycationService.showErrorNotify(res.data.message, 'COLLECTIONS.EDIT_OVERVIEW_FAILED'); + }); + + e.$options.hideable = true; + e.blur(); + } else { + e.$options.hideable = true; + e.blur(); + } + }, + onChange: function (e) { + e.$options.hideable = false; + }, + onShow: function (e) { + $('#' + e.$editor.attr('id') + ' .md-input').textcomplete([ + { // emoji strategy + match: /\B:([\-+\w]*)$/, + search: function (term, callback) { + callback($.map(window.emojies, function (emoji) { + return emoji.indexOf(term) === 0 ? emoji : null; + })); + }, + template: function (value) { + return '' + '' + value + ''; + }, + replace: function (value) { + return ':' + value + ': '; + }, + index: 1 + } + ]); + + e.setContent(c.overview); + $('#' + e.$editor.attr('id') + ' .md-input').attr('maxlength', vm.inputLengthConfig.collectionsOverviewLength); + + var elei = $('#' + e.$editor.attr('id') + ' .md-input'); + angular.element(elei).css('height', '200px'); + angular.element(elei).css('color', '#333'); + + var inputInfo = angular.element(''); + inputInfo.addClass('pull-right'); + inputInfo.addClass('input-length'); + inputInfo.text(e.getContent().length + '/' + vm.inputLengthConfig.collectionsOverviewLength); + $('#' + e.$editor.attr('id') + ' .md-header').append(inputInfo); + $('#' + e.$editor.attr('id') + ' .md-input').on('input propertychange', function (evt) { + inputInfo.text(e.getContent().length + '/' + vm.inputLengthConfig.collectionsOverviewLength); + }); + + var ele = $('#' + e.$editor.attr('id') + ' .md-footer'); + angular.element(ele).addClass('text-right'); + angular.element(ele[0].childNodes[0]).addClass('btn-width-80'); + ele[0].childNodes[0].innerText = $translate.instant('FORUMS.BTN_SAVE'); + + var cbtn = angular.element(''); + cbtn.bind('click', function (evt) { + e.setContent(c.overview); + e.$options.hideable = true; + e.blur(); + }); + ele.append(cbtn); + $compile(e.$editor.contents())($scope); + }, + onPreview: function (e) { + $('#' + e.$editor.attr('id') + ' .md-footer').css('display', 'none'); + }, + onPreviewEnd: function (e) { + $('#' + e.$editor.attr('id') + ' .md-footer').css('display', 'block'); + } + }); + }; + + /** + * vm.setRecommendLevel + */ + vm.setRecommendLevel = function (item, rl) { + CollectionsService.setRecommendLevel({ + _id: item._id, + rlevel: rl.value + }, function (res) { + vm.collection = res; + NotifycationService.showSuccessNotify('COLLECTIONS.SETRLEVEL_SUCCESSFULLY'); + }, function (res) { + NotifycationService.showSuccessNotify('COLLECTIONS.SETRLEVEL_ERROR'); + }); + }; + + /** + * removeFromCollections + * @param item + */ + vm.removeFromCollections = function (item) { + var modalOptions = { + closeButtonText: $translate.instant('COLLECTIONS.REMOVE_CONFIRM_CANCEL'), + actionButtonText: $translate.instant('COLLECTIONS.REMOVE_CONFIRM_OK'), + headerText: $translate.instant('COLLECTIONS.REMOVE_CONFIRM_HEADER_TEXT'), + bodyText: $translate.instant('COLLECTIONS.REMOVE_CONFIRM_BODY_TEXT') + }; + + ModalConfirmService.showModal({}, modalOptions) + .then(function (result) { + CollectionsService.removeFromCollection({ + collectionId: vm.collection._id, + torrentId: item._id + }, function (res) { + mtDebug.info(res); + vm.collection = res; + NotifycationService.showSuccessNotify('COLLECTIONS.REMOVE_SUCCESSFULLY'); + }, function (res) { + NotifycationService.showErrorNotify(res.data.message, 'COLLECTIONS.REMOVE_FAILED'); + }); + }); + }; + } +}()); diff --git a/modules/albums/client/controllers/albums.client.controller.js b/modules/albums/client/controllers/albums.client.controller.js new file mode 100644 index 00000000..c615c58a --- /dev/null +++ b/modules/albums/client/controllers/albums.client.controller.js @@ -0,0 +1,106 @@ +(function () { + 'use strict'; + + angular + .module('albums') + .controller('AlbumController', AlbumController); + + AlbumController.$inject = ['$scope', '$translate', 'getStorageLangService', 'MeanTorrentConfig', 'CollectionsService', 'NotifycationService', + 'DebugConsoleService']; + + function AlbumController($scope, $translate, getStorageLangService, MeanTorrentConfig, CollectionsService, NotifycationService, + mtDebug) { + var vm = this; + vm.lang = getStorageLangService.getLang(); + vm.appConfig = MeanTorrentConfig.meanTorrentConfig.app; + vm.itemsPerPageConfig = MeanTorrentConfig.meanTorrentConfig.itemsPerPage; + vm.tmdbConfig = MeanTorrentConfig.meanTorrentConfig.tmdbConfig; + + + /** + * buildPager + */ + vm.buildPager = function () { + vm.pagedItems = []; + vm.itemsPerPage = vm.itemsPerPageConfig.collectionsListPerPage; + vm.currentPage = 1; + + vm.figureOutItemsToDisplay(); + }; + + /** + * figureOutItemsToDisplay + * @param callback + */ + vm.figureOutItemsToDisplay = function (callback) { + vm.getCollectionsList(vm.currentPage, function (items) { + vm.filterLength = items.total; + vm.pagedItems = items.rows; + + if (callback) callback(); + }); + }; + + /** + * getCollectionsList + * @param p + * @param callback + */ + vm.getCollectionsList = function (p, callback) { + CollectionsService.get({ + skip: (p - 1) * vm.itemsPerPage, + limit: vm.itemsPerPage, + keys: vm.search + }, function (data) { + mtDebug.info(data); + callback(data); + }, function (err) { + NotifycationService.showErrorNotify(err.data.message, 'COLLECTIONS.LIST_ERROR'); + }); + }; + + /** + * getVoteAverage + * @param c + * @returns {number} + */ + vm.getVoteAverage = function (c) { + var total = 0; + var count = 0; + var avg = 0; + + angular.forEach(c.torrents, function (t) { + total += t.resource_detail_info.vote_average; + count += 1; + }); + avg = Math.floor((total / count) * 10) / 10; + + return avg || 0; + }; + + /** + * getMinMaxRelease + * @param c + * @returns {{min: *, max: *}} + */ + vm.getMinMaxRelease = function (c) { + var re = []; + + angular.forEach(c.torrents, function (t) { + re.push(parseInt(t.resource_detail_info.release_date, 10)); + }); + + return { + min: re.length > 0 ? Math.min.apply(null, re) : '', + max: re.length > 0 ? Math.max.apply(null, re) : '' + }; + }; + + /** + * pageChanged + */ + vm.pageChanged = function () { + vm.figureOutItemsToDisplay(); + }; + } +}()); diff --git a/modules/albums/client/services/albums.client.service.js b/modules/albums/client/services/albums.client.service.js new file mode 100644 index 00000000..c8060c33 --- /dev/null +++ b/modules/albums/client/services/albums.client.service.js @@ -0,0 +1,78 @@ +(function () { + 'use strict'; + + // Users service used for communicating with the users REST endpoint + angular + .module('albums.services') + .factory('AlbumsService', AlbumsService); + + AlbumsService.$inject = ['$resource', 'CacheFactory']; + + function AlbumsService($resource, CacheFactory) { + var albumsCache = CacheFactory.get('albumsCache') || CacheFactory.createCache('albumsCache'); + var removeCache = function (res) { + albumsCache.removeAll(); + return res.resource; + }; + + var album = $resource('/api/albums/:albumId', { + albumId: '@_id' + }, { + get: { + method: 'GET', + cache: albumsCache + }, + query: { + method: 'GET', + isArray: true, + cache: albumsCache + }, + update: { + method: 'PUT', + interceptor: {response: removeCache} + }, + save: { + method: 'POST', + interceptor: {response: removeCache} + }, + remove: { + method: 'DELETE', + interceptor: {response: removeCache} + }, + delete: { + method: 'DELETE', + interceptor: {response: removeCache} + }, + insertIntoAlbum: { + method: 'PUT', + url: '/api/albums/:albumId/insert/:torrentId', + params: { + albumId: '@albumId', + torrentId: '@torrentId' + }, + interceptor: {response: removeCache} + }, + removeFromAlbum: { + method: 'PUT', + url: '/api/albums/:albumId/remove/:torrentId', + params: { + albumId: '@albumId', + torrentId: '@torrentId' + }, + interceptor: {response: removeCache} + }, + setRecommendLevel: { + method: 'PUT', + url: '/api/albums/:albumId/set/recommendlevel/:rlevel', + params: { + albumId: '@_id', + rlevel: '@rlevel' + }, + interceptor: {response: removeCache} + } + + }); + + return album; + } +}()); diff --git a/modules/albums/client/views/album-view.client.view.html b/modules/albums/client/views/album-view.client.view.html new file mode 100644 index 00000000..e331929f --- /dev/null +++ b/modules/albums/client/views/album-view.client.view.html @@ -0,0 +1,104 @@ +
+
+
+
+
+
+ +

+ {{vm.collection.name}} +

+
+
+
+ +

+ {{vm.getMinMaxRelease().min}}-{{vm.getMinMaxRelease().max}} +

+
+
+
+
    +
  • {{'COLLECTIONS.FILES_NUMBERS' | translate}}: {{vm.collection.torrents.length}} +
  • +
  • {{'COLLECTIONS.VOTE_AVERAGE' | translate}}: + {{vm.collection.vote_average}} / {{vm.collection.vote_count}} {{ 'TMDB_FIELDS.VOTE_UNIT' | translate}} +
  • +
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+ + + +
+ + +
+ +
+
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + +
{{ 'TABLE_FIELDS.INFO' | translate}}{{ 'TABLE_FIELDS.VOTES' | translate}}{{ 'TABLE_FIELDS.LIFETIME' | translate}}{{ 'TABLE_FIELDS.SIZE' | translate}} + + {{ 'TABLE_FIELDS.SEEDS_LEECHERS_FINISHED' | translate}} + + {{ 'TABLE_FIELDS.PUBLISHER' | translate}}
+
+
+
+
+
\ No newline at end of file diff --git a/modules/albums/client/views/albums.client.view.html b/modules/albums/client/views/albums.client.view.html new file mode 100644 index 00000000..5b87ea93 --- /dev/null +++ b/modules/albums/client/views/albums.client.view.html @@ -0,0 +1,75 @@ +
+ + +
\ No newline at end of file diff --git a/modules/core/client/app/trans-string-en.js b/modules/core/client/app/trans-string-en.js index 3e359c98..88019142 100644 --- a/modules/core/client/app/trans-string-en.js +++ b/modules/core/client/app/trans-string-en.js @@ -239,6 +239,7 @@ MAKER: 'Resources Group', OPERLIST: 'Admin/Oper List', COLLECTIONS: 'Movie Collections', + ALBUMS: 'Albums', VIP: 'Vip', VIP_DONATE: 'Vip Donate', VIP_RULES: 'Vip Rules', diff --git a/modules/core/client/app/trans-string-zh-tw.js b/modules/core/client/app/trans-string-zh-tw.js index d0ef1539..7b124153 100644 --- a/modules/core/client/app/trans-string-zh-tw.js +++ b/modules/core/client/app/trans-string-zh-tw.js @@ -239,6 +239,7 @@ MAKER: '資源小組', OPERLIST: 'Admin/Oper 列表', COLLECTIONS: '電影系列', + ALBUMS: '資源專輯', VIP: 'Vip', VIP_DONATE: '捐贈VIP', VIP_RULES: 'Vip使用者協議', diff --git a/modules/core/client/app/trans-string-zh.js b/modules/core/client/app/trans-string-zh.js index 5caf3ecb..df2cd5bd 100644 --- a/modules/core/client/app/trans-string-zh.js +++ b/modules/core/client/app/trans-string-zh.js @@ -239,6 +239,7 @@ MAKER: '资源小组', OPERLIST: 'Admin/Oper 列表', COLLECTIONS: '电影系列', + ALBUMS: '资源专辑', VIP: 'Vip', VIP_DONATE: '捐赠VIP', VIP_RULES: 'Vip用户协议', diff --git a/modules/core/client/less/home.less b/modules/core/client/less/home.less index bdac4200..5aac86d9 100644 --- a/modules/core/client/less/home.less +++ b/modules/core/client/less/home.less @@ -421,7 +421,7 @@ .home-search { .search-title { color: #fff; - font-size: 2em; + font-size: 24px; font-weight: 400; margin-bottom: 0 !important; }