Re-wrote to support my api for requesting new movies.
							
								
								
									
										14
									
								
								dist/build.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										1
									
								
								dist/build.js.map
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								dist/no-image.png
									
									
									
									
										vendored
									
									
								
							
							
						
						| Before Width: | Height: | Size: 6.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								dist/placeholder.png
									
									
									
									
										vendored
									
									
								
							
							
						
						| Before Width: | Height: | Size: 2.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								dist/pulp-fiction.jpg
									
									
									
									
										vendored
									
									
								
							
							
						
						| Before Width: | Height: | Size: 279 KiB | 
							
								
								
									
										26
									
								
								index.html
									
									
									
									
									
								
							
							
						
						| @@ -5,11 +5,12 @@ | |||||||
|   "author": "Dmytro Barylo", |   "author": "Dmytro Barylo", | ||||||
|   "private": true, |   "private": true, | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", |     "dev": "cross-env NODE_ENV=development webpack-dev-server --hot", | ||||||
|     "build": "cross-env NODE_ENV=production webpack --progress --hide-modules", |     "build": "cross-env NODE_ENV=production webpack --progress --hide-modules", | ||||||
|     "start": "node server.js" |     "start": "node server.js" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "ag-grid-vue": "^17.0.0", | ||||||
|     "axios": "^0.15.3", |     "axios": "^0.15.3", | ||||||
|     "connect-history-api-fallback": "^1.3.0", |     "connect-history-api-fallback": "^1.3.0", | ||||||
|     "debounce": "^1.0.0", |     "debounce": "^1.0.0", | ||||||
| @@ -17,6 +18,7 @@ | |||||||
|     "numeral": "^2.0.4", |     "numeral": "^2.0.4", | ||||||
|     "vue": "^2.1.0", |     "vue": "^2.1.0", | ||||||
|     "vue-axios": "^1.2.2", |     "vue-axios": "^1.2.2", | ||||||
|  |     "vue-data-tablee": "^0.12.1", | ||||||
|     "vue-router": "^2.2.1" |     "vue-router": "^2.2.1" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						| @@ -3,19 +3,20 @@ | |||||||
|     <navigation></navigation> |     <navigation></navigation> | ||||||
|     <header class="header"> |     <header class="header"> | ||||||
|       <div class="header__search"> |       <div class="header__search"> | ||||||
|         <input class="header__search-input" type="text" v-model.trim="searchQuery" @keyup.enter="search" @blur="search" placeholder="Search for a movie..."> |         <input class="header__search-input" type="text" v-model.trim="searchQuery" @keyup.enter="search" @blur="search" placeholder="Search for a movie or show..."> | ||||||
|         <svg class="header__search-icon"> |         <svg class="header__search-icon"> | ||||||
|           <use xlink:href="#iconSearch"></use> |           <use xlink:href="#iconSearch"></use> | ||||||
|         </svg> |         </svg> | ||||||
|       </div> |       </div> | ||||||
|     </header> |     </header> | ||||||
|  |  | ||||||
|       <movie-popup v-if="moviePopupIsVisible" @close="closeMoviePopup" :id="moviePopupId"></movie-popup> |       <movie-popup v-if="moviePopupIsVisible" @close="closeMoviePopup" :id="moviePopupId" :type="moviePopupType"></movie-popup> | ||||||
|  |  | ||||||
|     <section class="main"> |     <section class="main"> | ||||||
|       <transition name="fade" @after-leave="afterLeave"> |       <transition name="fade" @after-leave="afterLeave"> | ||||||
|         <router-view name="list-router-view" :type="'page'" :mode="'collection'" :key="$route.params.category"></router-view> |         <router-view name="list-router-view" :type="'page'" :mode="'collection'" :key="$route.params.category"></router-view> | ||||||
|         <router-view name="search-router-view" :type="'page'" :mode="'search'" :key="$route.params.query"></router-view> |         <router-view name="search-router-view" :type="'page'" :mode="'search'" :key="$route.params.query"></router-view> | ||||||
|  |         <router-view name="user-requests-router-view" :type="'page'" :mode="'user-requests'"></router-view> | ||||||
|         <router-view name="page-router-view"></router-view> |         <router-view name="page-router-view"></router-view> | ||||||
|       </transition> |       </transition> | ||||||
|     </section> |     </section> | ||||||
| @@ -36,6 +37,7 @@ export default { | |||||||
|       moviePopupIsVisible: false, |       moviePopupIsVisible: false, | ||||||
|       moviePopupHistoryVisible: false, |       moviePopupHistoryVisible: false, | ||||||
|       moviePopupId: 0, |       moviePopupId: 0, | ||||||
|  |       moviePopupType: 'movie', | ||||||
|       searchQuery: '' |       searchQuery: '' | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
| @@ -58,17 +60,20 @@ export default { | |||||||
|       }.bind(this)); |       }.bind(this)); | ||||||
|     }, |     }, | ||||||
|     setUserStatus(){ |     setUserStatus(){ | ||||||
|       storage.sessionId = localStorage.getItem('session_id') || null; |       storage.token = localStorage.getItem('token') || null; | ||||||
|       storage.userId = localStorage.getItem('user_id') || null; |       storage.username = localStorage.getItem('username') || null; | ||||||
|  |       storage.admin = localStorage.getItem('admin') || null; | ||||||
|     }, |     }, | ||||||
|     // Movie Popup Methods |     // Movie Popup Methods | ||||||
|     openMoviePopup(id, newMoviePopup){ |     openMoviePopup(id, type, newMoviePopup){ | ||||||
|  |       console.log('app openMoviePopup:', type) | ||||||
|       if(newMoviePopup){ |       if(newMoviePopup){ | ||||||
|         storage.backTitle = document.title; |         storage.backTitle = document.title; | ||||||
|       } |       } | ||||||
|       storage.createMoviePopup = newMoviePopup; |       storage.createMoviePopup = newMoviePopup; | ||||||
|       this.moviePopupIsVisible = true; |       this.moviePopupIsVisible = true; | ||||||
|       this.moviePopupId = id; |       this.moviePopupId = id; | ||||||
|  |       this.moviePopupType = type; | ||||||
|       document.querySelector('body').classList.add('hidden'); |       document.querySelector('body').classList.add('hidden'); | ||||||
|     }, |     }, | ||||||
|     closeMoviePopup(){ |     closeMoviePopup(){ | ||||||
| @@ -232,8 +237,8 @@ img{ | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     &-icon{ |     &-icon{ | ||||||
|       width: 14px; |       width: 19px; | ||||||
|       height: 14px; |       height: 19px; | ||||||
|       fill: rgba($c-dark, 0.5); |       fill: rgba($c-dark, 0.5); | ||||||
|       transition: fill 0.5s ease; |       transition: fill 0.5s ease; | ||||||
|       pointer-events: none; |       pointer-events: none; | ||||||
| @@ -288,7 +293,11 @@ img{ | |||||||
|     font-size: 12px; |     font-size: 12px; | ||||||
|     padding: 6px 20px 5px 20px; |     padding: 6px 20px 5px 20px; | ||||||
|   } |   } | ||||||
|   body:not(.touch) &:hover{ |   &:active, &:hover{ | ||||||
|  |     background: $c-dark; | ||||||
|  |     color: $c-white; | ||||||
|  |   } | ||||||
|  |   body:not(.touch) &:hover, &:focus{ | ||||||
|     background: $c-dark; |     background: $c-dark; | ||||||
|     color: $c-white; |     color: $c-white; | ||||||
|   } |   } | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								src/assets/arrival.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.0 MiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/disaster-artist.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 139 KiB | 
| Before Width: | Height: | Size: 279 KiB After Width: | Height: | Size: 275 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/star-wars.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 423 KiB | 
| @@ -39,12 +39,19 @@ export default { | |||||||
|     height: 100%; |     height: 100%; | ||||||
|     background: rgba($c-light, 0.7); |     background: rgba($c-light, 0.7); | ||||||
|   } |   } | ||||||
|  |   &-shortList{ | ||||||
|  |     width: 100%; | ||||||
|  |   } | ||||||
|   &__content{ |   &__content{ | ||||||
|     width: 100%; |     width: 100%; | ||||||
|     padding: 0 20px; |     padding: 0 20px; | ||||||
|     text-align: center; |     text-align: center; | ||||||
|     @include tablet-min{ |     @include tablet-min{ | ||||||
|       padding: 0 40px 40px 0; |       padding: 20px 0 0 0; | ||||||
|  |     } | ||||||
|  |     &-shortList { | ||||||
|  |       width: 100%; | ||||||
|  |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|     &__title{ |     &__title{ | ||||||
|   | |||||||
| @@ -1,18 +1,12 @@ | |||||||
| <template> | <template> | ||||||
|   <section class="home"> |   <section class="home"> | ||||||
|     <header class="home__header"> |     <header class="home__header" v-bind:style="{ 'background-image': 'url(' + imageFile + ')' }"> | ||||||
|       <div class="home__header-wrap"> |       <div class="home__header-wrap"> | ||||||
|         <h1 class="home__header-title">The Movie DB App</h1> |         <h1 class="home__header-title">Request new movies or tv shows for plex</h1> | ||||||
|         <strong class="home__header-subtitle">Made with Vue.js</strong> |         <strong class="home__header-subtitle">Made with Vue.js</strong> | ||||||
|         <a href="https://github.com/dmtrbrl/tmdb-app" target="_blank" class="home__header-link"> |  | ||||||
|           <svg class="home__header-link-icon"> |  | ||||||
|             <use xlink:href="#iconGithub"></use> |  | ||||||
|           </svg> |  | ||||||
|           <span>View Code</span> |  | ||||||
|         </a> |  | ||||||
|       </div> |       </div> | ||||||
|     </header> |     </header> | ||||||
|     <movies-list v-for="item in listTypes" v-if="item.isCategory" :type="'component'" :mode="'collection'" :category="item.query" :shortList="true"></movies-list> |     <movies-list v-for="item in listTypes" v-if="item.isCategory" :type="'component'" :mode="item.type" :category="item.query" :shortList="true"></movies-list> | ||||||
|   </section> |   </section> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| @@ -25,7 +19,8 @@ export default { | |||||||
|   components: { MoviesList }, |   components: { MoviesList }, | ||||||
|   data(){ |   data(){ | ||||||
|     return { |     return { | ||||||
|       listTypes: storage.listTypes |       listTypes: storage.listTypes, | ||||||
|  |       imageFile: 'dist/pulp-fiction.jpg' | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   created(){ |   created(){ | ||||||
| @@ -50,9 +45,9 @@ export default { | |||||||
|     background-position: 50% 50%; |     background-position: 50% 50%; | ||||||
|     position: relative; |     position: relative; | ||||||
|     background-color: $c-dark; |     background-color: $c-dark; | ||||||
|     background-image: url('~assets/pulp-fiction.jpg'); |     background-image: url('~assets/arrival.jpg'); | ||||||
|     @include tablet-min{ |     @include tablet-min{ | ||||||
|       height: 384px; |       height: 284px; | ||||||
|     } |     } | ||||||
|     &:before{ |     &:before{ | ||||||
|       content: ""; |       content: ""; | ||||||
|   | |||||||
| @@ -10,44 +10,96 @@ | |||||||
|           <div class="movie__title"> |           <div class="movie__title"> | ||||||
|             <h1 class="movie__title-text"> |             <h1 class="movie__title-text"> | ||||||
|               {{ movie.title }} |               {{ movie.title }} | ||||||
|               <span v-if="movie.tagline">{{ movie.tagline }}</span> |               <!-- <span>{{ movie.type }}</span> --> | ||||||
|             </h1> |             </h1> | ||||||
|  |             <span> | ||||||
|  |             </span> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </header> |       </header> | ||||||
|       <div class="movie__main"> |       <div class="movie__main"> | ||||||
|         <div class="movie__wrap movie__wrap--main" :class="{'movie__wrap--page': type=='page'}"> |         <div class="movie__wrap movie__wrap--main" :class="{'movie__wrap--page': type=='page'}"> | ||||||
|           <div class="movie__actions" v-if="userLoggedIn && favoriteChecked"> |           <!-- <div class="movie__actions" v-if="userLoggedIn && favoriteChecked"> --> | ||||||
|             <a href="#" class="movie__actions-link" :class="{'active' : favorite === true}" @click.prevent="toggleFavorite"> |           <div class="movie__actions"> | ||||||
|               <svg class="movie__actions-icon" :class="{'waiting' : favorite === ''}"> |  | ||||||
|                 <use xlink:href="#iconFavorite"></use> |             <a class="movie__actions-link" v-if="matched" :class="{'active' : matched}"> | ||||||
|  |               <svg class="movie__actions-icon"> | ||||||
|  |                 <use xlink:href="#iconExsits"></use> | ||||||
|               </svg> |               </svg> | ||||||
|               <span class="movie__actions-text" v-if="favorite === ''">Wait...</span> |               <span class="movie__actions-text"> Already in plex  🎉</span> | ||||||
|               <span class="movie__actions-text" v-else-if="favorite">Marked as Favorite</span> |             </a> | ||||||
|               <span class="movie__actions-text" v-else>Mark as Favorite?</span> |             <a class="movie__actions-link" v-else="matched"> | ||||||
|  |               <svg class="movie__actions-icon"> | ||||||
|  |                 <use xlink:href="#iconNot_exsits"></use> | ||||||
|  |               </svg> | ||||||
|  |               <span class="movie__actions-text"> Not in plex yet</span> | ||||||
|  |             </a> | ||||||
|  |  | ||||||
|  |             <a class="movie__actions-link" :class="{'active' : requested}" v-if="this.requested"> | ||||||
|  |               <svg class="movie__actions-icon"> | ||||||
|  |                 <use xlink:href="#iconSent"></use> | ||||||
|  |               </svg> | ||||||
|  |               <span class="movie__actions-text"> Requested to be downloaded</span> | ||||||
|  |             </a> | ||||||
|  |             <a class="movie__actions-link" v-else="this.requested"  @click.prevent="sendRequest"> | ||||||
|  |               <svg class="movie__actions-icon" :class="{'waiting' : requested}"> | ||||||
|  |                 <use xlink:href="#iconUnmatched"></use> | ||||||
|  |               </svg> | ||||||
|  |               <span class="movie__actions-text"> Request to be downloaded?</span> | ||||||
|  |             </a> | ||||||
|  |  | ||||||
|  |             <a class="movie__actions-link" @click="showTorrents=true" v-if="admin==='true'" :class="{'active' : showTorrents}"> | ||||||
|  |               <svg class="movie__actions-icon"> | ||||||
|  |                 <use xlink:href="#icon_torrents"></use> | ||||||
|  |               </svg> | ||||||
|  |               <span class="movie__actions-text"> Search for torrents</span> | ||||||
|  |             </a> | ||||||
|  |  | ||||||
|  |             <a class="movie__actions-link" @click.prevent="openTmdb"> | ||||||
|  |               <svg class="movie__actions-icon"> | ||||||
|  |                 <use xlink:href="#icon_info"></use> | ||||||
|  |               </svg> | ||||||
|  |               <span class="movie__actions-text"> See more info</span> | ||||||
|             </a> |             </a> | ||||||
|           </div> |           </div> | ||||||
|           <div class="movie__info"> |           <div class="movie__info"> | ||||||
|             <div v-if="movie.overview" class="movie__description"> |             <div v-if="movie.summary" class="movie__description"> | ||||||
|               {{ movie.overview }} |               {{ movie.summary }} | ||||||
|             </div> |             </div> | ||||||
|             <div class="movie__details"> |             <div class="movie__details"> | ||||||
|               <div v-if="movie.genres.length" class="movie__details-block"> |              <!--  <div v-if="movie.genres.length" class="movie__details-block"> | ||||||
|                 <h2 class="movie__details-title"> |                 <h2 class="movie__details-title"> | ||||||
|                   Genres |                   Genres | ||||||
|                 </h2> |                 </h2> | ||||||
|                 <div class="movie__details-text"> |                 <div class="movie__details-text"> | ||||||
|                   {{ nestedDataToString(movie.genres) }} |                   {{ nestedDataToString(movie.genres) }} | ||||||
|                 </div> |                 </div> | ||||||
|               </div> |               </div> --> | ||||||
|               <div v-if="movie.release_date" class="movie__details-block"> |               <div v-if="movie.year" class="movie__details-block"> | ||||||
|                 <h2 class="movie__details-title"> |                 <h2 class="movie__details-title"> | ||||||
|                   Release Date |                   Release Date | ||||||
|                 </h2> |                 </h2> | ||||||
|                 <div class="movie__details-text" v-formatDate="movie.release_date"></div> |                 <div class="movie__details-text"> | ||||||
|  |                   {{ movie.year }} | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |               <div v-if="movie.type == 'show'" class="movie__details-block"> | ||||||
|  |                 <h2 class="movie__details-title"> | ||||||
|  |                   Seasons | ||||||
|  |                 </h2> | ||||||
|  |                 <div class="movie__details-text"> | ||||||
|  |                   10 | ||||||
|  |                 </div> | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|  |  | ||||||
|  |           <!-- <TableDemo class="movie__admin">This is it</TableDemo> --> | ||||||
|  |          | ||||||
|  |           <div class="movie__admin" v-if="admin == 'true' && showTorrents"> | ||||||
|  |               <h2 class="movie__admin-title">torrents: {{ movie.title }}</h2> | ||||||
|  |               <TorrentList :query="movie.title"></TorrentList> | ||||||
|  |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
| @@ -59,9 +111,11 @@ import axios from 'axios' | |||||||
| import storage from '../storage.js' | import storage from '../storage.js' | ||||||
| import img from '../directives/v-image.js' | import img from '../directives/v-image.js' | ||||||
| import formatDate from '../directives/v-formatDate.js' | import formatDate from '../directives/v-formatDate.js' | ||||||
|  | import TorrentList from './TorrentList.vue' | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   props: ['id', 'type'], |   props: ['id', 'type', 'mediaType'], | ||||||
|  |   components: { TorrentList }, | ||||||
|   directives: { |   directives: { | ||||||
|     img: img, |     img: img, | ||||||
|     formatDate: formatDate |     formatDate: formatDate | ||||||
| @@ -74,30 +128,40 @@ export default { | |||||||
|       movieBackdropSrc: '', |       movieBackdropSrc: '', | ||||||
|       userLoggedIn: storage.sessionId ? true : false, |       userLoggedIn: storage.sessionId ? true : false, | ||||||
|       favoriteChecked: false, |       favoriteChecked: false, | ||||||
|       favorite: '' |       requested: false, | ||||||
|  |       admin: localStorage.getItem('admin'), | ||||||
|  |       showTorrents: false | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   computed: { | ||||||
|  |     loaded(){ | ||||||
|  |       return this.movieLoaded ? true : false; | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   // computed: { |  | ||||||
|   //   loaded(){ |  | ||||||
|   //     return this.movieLoaded ? true : false; |  | ||||||
|   //   } |  | ||||||
|   // }, |  | ||||||
|   methods: { |   methods: { | ||||||
|     fetchMovie(id){ |     fetchMovie(id){ | ||||||
|       axios.get(`https://api.themoviedb.org/3/movie/${id}?api_key=${storage.apiKey}&language=en-US`) |       this.id = id; | ||||||
|  |       (this.mediaType == 'show') ? this.tmdbType = 'show' : this.tmdbType = 'movie' | ||||||
|  |       // Fetch from seasoned to get matched status | ||||||
|  |       // axios.get(`https://apollo.kevinmidboe.com/api/v1/plex/request/${id}?type=${'show'}&api_key=${storage.apiKey}&language=en-US`) | ||||||
|  |       axios.get(`https://api.kevinmidboe.com/api/v1/plex/request/${id}?type=${this.mediaType}`) | ||||||
|  |       // axios.get(`http://localhost:31459/api/v1/plex/request/${id}?type=${this.mediaType}&api_key=${storage.apiKey}&language=en-US`) | ||||||
|       .then(function(resp){ |       .then(function(resp){ | ||||||
|           let movie = resp.data; |           let movie = resp.data; | ||||||
|           this.movie = movie; |           this.movie = movie; | ||||||
|           this.poster(); |           this.poster(); | ||||||
|           this.backdrop(); |           this.backdrop(); | ||||||
|  |           this.matched = this.movie.matchedInPlex; | ||||||
|  |           this.requested = this.movie.requested; | ||||||
|           if(this.userLoggedIn){ |           if(this.userLoggedIn){ | ||||||
|             this.checkIfInFavorites(movie.id); |             this.checkIfInFavorites(movie.id); | ||||||
|  |             this.movieLoaded = true; | ||||||
|           } else { |           } else { | ||||||
|             this.movieLoaded = true; |             this.movieLoaded = true; | ||||||
|           } |           } | ||||||
|           // Push state |           // Push state | ||||||
|           if(storage.createMoviePopup){ |           if(storage.createMoviePopup){ | ||||||
|             storage.moviePath = '/movie/' + id; |             storage.moviePath = '/request/' + this.mediaType + '/' + id; | ||||||
|             history.pushState({ popup: true }, null, storage.moviePath); |             history.pushState({ popup: true }, null, storage.moviePath); | ||||||
|             storage.createMoviePopup = false; |             storage.createMoviePopup = false; | ||||||
|           } |           } | ||||||
| @@ -105,17 +169,19 @@ export default { | |||||||
|           document.title = this.movie.title + storage.pageTitlePostfix; |           document.title = this.movie.title + storage.pageTitlePostfix; | ||||||
|       }.bind(this)) |       }.bind(this)) | ||||||
|       .catch(function(error) { |       .catch(function(error) { | ||||||
|  |         console.log(error.response) | ||||||
|         this.$router.push({ name: '404' }); |         this.$router.push({ name: '404' }); | ||||||
|       }.bind(this)); |       }.bind(this)); | ||||||
|     }, |     }, | ||||||
|     poster() { |     poster() { | ||||||
|  |       // Change the poster resolution | ||||||
|       if(this.movie.poster_path){ |       if(this.movie.poster_path){ | ||||||
|         this.moviePosterSrc = 'https://image.tmdb.org/t/p/w600_and_h900_bestv2' + this.movie.poster_path; |         this.moviePosterSrc = 'https://image.tmdb.org/t/p/w300' + this.movie.poster_path; | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     backdrop(){ |     backdrop(){ | ||||||
|       if(this.movie.backdrop_path){ |       if(this.movie.background_path){ | ||||||
|         this.movieBackdropSrc = 'https://image.tmdb.org/t/p/w500' + this.movie.backdrop_path; |         this.movieBackdropSrc = 'https://image.tmdb.org/t/p/w500' + this.movie.background_path; | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     nestedDataToString(data) { |     nestedDataToString(data) { | ||||||
| @@ -125,13 +191,15 @@ export default { | |||||||
|       return resultString; |       return resultString; | ||||||
|     }, |     }, | ||||||
|     checkIfInFavorites(id){ |     checkIfInFavorites(id){ | ||||||
|       axios.get(`https://api.themoviedb.org/3/movie/${id}/account_states?api_key=${storage.apiKey}&session_id=${storage.sessionId}`) |       // Change to check in plex | ||||||
|  |       axios.get(`https://api.themoviedb.org/3/${this.tmdbType}/${id}/account_states?api_key=${storage.apiKey}&session_id=${storage.sessionId}`) | ||||||
|       .then(function(resp){ |       .then(function(resp){ | ||||||
|           this.favorite = resp.data.favorite; |           this.favorite = resp.data.favorite; | ||||||
|           this.favoriteChecked = true; |           this.favoriteChecked = true; | ||||||
|           this.movieLoaded = true; |           this.movieLoaded = true; | ||||||
|       }.bind(this)) |       }.bind(this)) | ||||||
|     }, |     }, | ||||||
|  |     // Toggle the downloading status if admin | ||||||
|     toggleFavorite(){ |     toggleFavorite(){ | ||||||
|       let favoriteInvert = !this.favorite; |       let favoriteInvert = !this.favorite; | ||||||
|       this.favorite = ''; |       this.favorite = ''; | ||||||
| @@ -144,7 +212,28 @@ export default { | |||||||
|         this.favorite = favoriteInvert; |         this.favorite = favoriteInvert; | ||||||
|         eventHub.$emit('updateFavorite'); |         eventHub.$emit('updateFavorite'); | ||||||
|       }.bind(this)); |       }.bind(this)); | ||||||
|     } |     }, | ||||||
|  |     // Send a request for a specific movie | ||||||
|  |     sendRequest(){ | ||||||
|  |       this.requested = '' | ||||||
|  |       axios.post(`https://api.kevinmidboe.com/api/v1/plex/request/${this.id}?type=${this.mediaType}`, | ||||||
|  |         { headers: {authorization: storage.token} }, | ||||||
|  |       ) | ||||||
|  |       // axios.post(`https://api.kevinmidboe.com/api/v1/plex/request/${this.id}?api_key=${storage.apiKey}&session_id=${storage.sessionId}`, { | ||||||
|  |       .then(function(resp){ | ||||||
|  |         if (resp.data.success) | ||||||
|  |           this.requested = true; | ||||||
|  |         else | ||||||
|  |           this.requested = false; | ||||||
|  |       }.bind(this)); | ||||||
|  |     }, | ||||||
|  |     openTmdb(){ | ||||||
|  |       window.location.replace('https://www.themoviedb.org/' + this.tmdbType + '/' + this.id) | ||||||
|  |     }, | ||||||
|  |     // Search torrents by query | ||||||
|  |     // searchForTorrents() { | ||||||
|  |     //   axios.get(`https://apollo.kevinmidboe.com/api/v1/plex/request/${id}?type=${'movie'}&api_key=${storage.apiKey}&language=en-US`) | ||||||
|  |     // }, | ||||||
|   }, |   }, | ||||||
|   watch: { |   watch: { | ||||||
|     id: function(val){ |     id: function(val){ | ||||||
| @@ -153,6 +242,7 @@ export default { | |||||||
|   }, |   }, | ||||||
|   created(){ |   created(){ | ||||||
|     this.fetchMovie(this.id); |     this.fetchMovie(this.id); | ||||||
|  |     console.log('admin: ', this.admin) | ||||||
|   } |   } | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
| @@ -292,19 +382,26 @@ export default { | |||||||
|         &.active{ |         &.active{ | ||||||
|           color: $c-dark; |           color: $c-dark; | ||||||
|         } |         } | ||||||
|  |         &.pending{ | ||||||
|  |           color: #f8bd2d; | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|       &-icon{ |       &-icon{ | ||||||
|         width: 16px; |         width: 18px; | ||||||
|         height: 16px; |         height: 18px; | ||||||
|         margin: 0 10px 0 0; |         margin: 0 10px 0 0; | ||||||
|         fill: rgba($c-dark, 0.5); |         fill: rgba($c-dark, 0.5); | ||||||
|         transition: fill 0.5s ease, transform 0.5s ease; |         transition: fill 0.5s ease, transform 0.5s ease; | ||||||
|         &.waiting{ |         &.waiting{ | ||||||
|           transform: scale(0.8, 0.8); |           transform: scale(0.8, 0.8); | ||||||
|         } |         } | ||||||
|  |         &.pending{ | ||||||
|  |           fill: #f8bd2d; | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|       &-link:hover &-icon{ |       &-link:hover &-icon{ | ||||||
|         fill: rgba($c-dark, 0.75); |         fill: rgba($c-dark, 0.75); | ||||||
|  |         cursor: pointer; | ||||||
|       } |       } | ||||||
|       &-link.active &-icon{ |       &-link.active &-icon{ | ||||||
|         fill: $c-green; |         fill: $c-green; | ||||||
| @@ -312,6 +409,7 @@ export default { | |||||||
|       &-text{ |       &-text{ | ||||||
|         display: block; |         display: block; | ||||||
|         padding-top: 2px; |         padding-top: 2px; | ||||||
|  |         cursor: pointer; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     &__info{ |     &__info{ | ||||||
| @@ -339,10 +437,15 @@ export default { | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       &__details{ |       &__details{ | ||||||
|  |         &-block{ | ||||||
|  |           float: left; | ||||||
|  |         } | ||||||
|         &-block:not(:last-child){ |         &-block:not(:last-child){ | ||||||
|           margin-bottom: 20px; |           margin-bottom: 20px; | ||||||
|  |           margin-right: 20px; | ||||||
|           @include tablet-min{ |           @include tablet-min{ | ||||||
|             margin-bottom: 30px; |             margin-bottom: 30px; | ||||||
|  |             margin-right: 30px; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         &-title{ |         &-title{ | ||||||
| @@ -361,5 +464,28 @@ export default { | |||||||
|           margin-top: 5px; |           margin-top: 5px; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |     &__admin{ | ||||||
|  |       width: 100%; | ||||||
|  |       padding: 20px; | ||||||
|  |       order: 2; | ||||||
|  |       @include tablet-min{ | ||||||
|  |         order: 3; | ||||||
|  |         padding: 40px; | ||||||
|  |         padding-top: 0px; | ||||||
|  |         width: 100%; | ||||||
|  |       } | ||||||
|  |       &-title{ | ||||||
|  |           margin: 0; | ||||||
|  |           font-weight: 400; | ||||||
|  |           text-transform: uppercase; | ||||||
|  |           text-align: center; | ||||||
|  |           font-size: 14px; | ||||||
|  |           color: $c-green; | ||||||
|  |           padding-bottom: 20px; | ||||||
|  |           @include tablet-min{ | ||||||
|  |             font-size: 16px; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| </style> | </style> | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| <template> | <template> | ||||||
|   <div class="movie-popup" @click="$emit('close')"> |   <div class="movie-popup" @click="$emit('close')"> | ||||||
|     <div class="movie-popup__box" @click.stop> |     <div class="movie-popup__box" @click.stop> | ||||||
|       <movie :id="id"></movie> |       <movie :id="id" :mediaType="type"></movie> | ||||||
|       <button class="movie-popup__close" @click="$emit('close')"></button> |       <button class="movie-popup__close" @click="$emit('close')"></button> | ||||||
|     </div> |     </div> | ||||||
|     <i class="loader"></i> |     <i class="loader"></i> | ||||||
| @@ -12,7 +12,7 @@ | |||||||
| import Movie from './Movie.vue'; | import Movie from './Movie.vue'; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   props: ['id'], |   props: ['id', 'type'], | ||||||
|   components: { Movie }, |   components: { Movie }, | ||||||
|   created(){ |   created(){ | ||||||
|     window.addEventListener('keyup', function(e){ |     window.addEventListener('keyup', function(e){ | ||||||
|   | |||||||
| @@ -4,7 +4,10 @@ | |||||||
|       <header class="movies__header"> |       <header class="movies__header"> | ||||||
|         <h2 class="movies__title">{{ listTitle }}</h2> |         <h2 class="movies__title">{{ listTitle }}</h2> | ||||||
|         <span class="movies__results" v-if="!shortList">{{ countResults }}</span> |         <span class="movies__results" v-if="!shortList">{{ countResults }}</span> | ||||||
|         <router-link v-if="shortList" class="movies__link" :to="{name: 'home-category', params: {category: category}}"> |         <router-link v-if="shortList && mode != 'user-requests'" class="movies__link" :to="{name: 'home-category', params: {category: category}}"> | ||||||
|  |           View All | ||||||
|  |         </router-link> | ||||||
|  |          <router-link v-if="shortList && mode == 'user-requests'" class="movies__link" :to="{name: 'user-requests'}"> | ||||||
|           View All |           View All | ||||||
|         </router-link> |         </router-link> | ||||||
|       </header> |       </header> | ||||||
| @@ -16,7 +19,7 @@ | |||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|     <i v-if="!listLoaded" class="loader"></i> |     <i v-if="!listLoaded" class="loader"></i> | ||||||
|     <section v-if="!movies.length" class="not-found"> |     <section v-if="!movies.length && !shortList" class="not-found"> | ||||||
|       <div class="not-found__content"> |       <div class="not-found__content"> | ||||||
|         <h2 class="not-found__title" v-if="mode == 'search'">Nothing Found</h2> |         <h2 class="not-found__title" v-if="mode == 'search'">Nothing Found</h2> | ||||||
|         <h2 class="not-found__title" v-if="mode == 'favorite'">You haven't added any favorite movies</h2> |         <h2 class="not-found__title" v-if="mode == 'favorite'">You haven't added any favorite movies</h2> | ||||||
| @@ -61,13 +64,18 @@ export default { | |||||||
|       return this.$route.params.query || ''; |       return this.$route.params.query || ''; | ||||||
|     }, |     }, | ||||||
|     request(){ |     request(){ | ||||||
|  |       console.log('todays mode is: ', this.mode); | ||||||
|       if(this.mode == 'search'){ |       if(this.mode == 'search'){ | ||||||
|         return `https://api.themoviedb.org/3/search/movie?api_key=${storage.apiKey}&language=en-US&query=${this.query}&page=${this.currentPage}`; |         return `https://api.kevinmidboe.com/api/v1/plex/request?query=${this.query}&page=${this.currentPage}`; | ||||||
|  |       } else if(this.mode == 'requests' || this.$route.params.category == 'requests') { | ||||||
|  |         return `https://api.kevinmidboe.com/api/v1/plex/requests/all?page=${this.currentPage}&status=requested`; | ||||||
|       } else if(this.mode == 'collection') { |       } else if(this.mode == 'collection') { | ||||||
|         let caregory = this.$route.params.category || this.category; |         let category = this.$route.params.category || this.category; | ||||||
|         return `https://api.themoviedb.org/3/movie/${caregory}?api_key=${storage.apiKey}&language=en-US&page=${this.currentPage}`; |         return `https://api.kevinmidboe.com/api/v1/tmdb/list/${category}?page=${this.currentPage}`; | ||||||
|       } else if(this.mode == 'favorite') { |       } else if(this.mode == 'history') { | ||||||
|         return `https://api.themoviedb.org/3/account/${storage.userId}/favorite/movies?api_key=${storage.apiKey}&session_id=${storage.sessionId}&language=en-US&sort_by=created_at.desc&page=${this.currentPage}`; |         return 'https://api.kevinmidboe.com/api/v1/user/history'; | ||||||
|  |       } else if(this.mode == 'user-requests') { | ||||||
|  |         return 'https://api.kevinmidboe.com/api/v1/user/requests'; | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     countResults(){ |     countResults(){ | ||||||
| @@ -80,9 +88,13 @@ export default { | |||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     fetchCategory(){ |     fetchCategory(){ | ||||||
|       axios.get(this.request) |       axios.get(this.request, { | ||||||
|  |         headers: {authorization: storage.token}, | ||||||
|  |       }) | ||||||
|       .then(function(resp){ |       .then(function(resp){ | ||||||
|           let data = resp.data; |           let data = resp.data; | ||||||
|  |           console.log('data: ', data) | ||||||
|  |  | ||||||
|           if(this.shortList){ |           if(this.shortList){ | ||||||
|             this.movies = data.results.slice(0, 5); |             this.movies = data.results.slice(0, 5); | ||||||
|             this.pages = 1; |             this.pages = 1; | ||||||
| @@ -144,11 +156,15 @@ export default { | |||||||
|     if(this.mode == 'search'){ |     if(this.mode == 'search'){ | ||||||
|       this.listTitle = storage.categories['search']; |       this.listTitle = storage.categories['search']; | ||||||
|       eventHub.$emit('setSearchQuery'); |       eventHub.$emit('setSearchQuery'); | ||||||
|  |     } else if(this.mode == 'requests') { | ||||||
|  |       this.listTitle = storage.categories['requests']; | ||||||
|     } else if(this.mode == 'collection') { |     } else if(this.mode == 'collection') { | ||||||
|       let caregory = this.$route.params.category || this.category; |       let category = this.$route.params.category || this.category; | ||||||
|       this.listTitle = storage.categories[caregory]; |       this.listTitle = storage.categories[category]; // <-- this | ||||||
|     } else if(this.mode == 'favorite') { |     } else if(this.mode == 'favorite') { | ||||||
|       this.listTitle = storage.categories['favorite']; |       this.listTitle = storage.categories['favorite']; | ||||||
|  |     } else if(this.mode == 'user-requests') { | ||||||
|  |       this.listTitle = storage.categories['user-requests']; | ||||||
|     } |     } | ||||||
|     this.fetchCategory(); |     this.fetchCategory(); | ||||||
|     eventHub.$on('updateFavorite', this.updateFavorite); |     eventHub.$on('updateFavorite', this.updateFavorite); | ||||||
|   | |||||||
| @@ -1,12 +1,13 @@ | |||||||
| <template> | <template> | ||||||
|   <li class="movies-item"> |   <li class="movies-item"> | ||||||
|     <a class="movies-item__link" :class="{'no-image': noImage}" :href="'/movie/' + movie.id" @click.prevent="openMoviePopup(movie.id, true)"> |     <a class="movies-item__link" :class="{'no-image': noImage}" href="" @click.prevent="openMoviePopup(movie.id, movie.type, true)"> | ||||||
|       <figure class="movies-item__poster"> |       <figure class="movies-item__poster"> | ||||||
|         <img v-if="!noImage" class="movies-item__img" src="~assets/placeholder.png" v-img="poster()" alt=""> |         <img v-if="!noImage" class="movies-item__img" src="~assets/placeholder.png" v-img="poster()" alt=""> | ||||||
|         <img v-if="noImage" class="movies-item__img is-loaded" src="~assets/no-image.png" alt=""> |         <img v-if="noImage" class="movies-item__img is-loaded" src="~assets/no-image.png" alt=""> | ||||||
|       </figure> |       </figure> | ||||||
|       <div class="movies-item__content"> |       <div class="movies-item__content"> | ||||||
|         <p class="movies-item__title">{{ movie.title }}</p> |         <p class="movies-item__title">{{ movie.title }}</p> | ||||||
|  |         <p class="movies-item__title">{{ movie.year }}</p> | ||||||
|       </div> |       </div> | ||||||
|     </a> |     </a> | ||||||
|   </li> |   </li> | ||||||
| @@ -28,13 +29,14 @@ export default { | |||||||
|   methods: { |   methods: { | ||||||
|     poster() { |     poster() { | ||||||
|       if(this.movie.poster_path){ |       if(this.movie.poster_path){ | ||||||
|         return 'https://image.tmdb.org/t/p/w370_and_h556_bestv2' + this.movie.poster_path; |         return 'https://image.tmdb.org/t/p/w300' + this.movie.poster_path; | ||||||
|       } else { |       } else { | ||||||
|         this.noImage = true; |         this.noImage = true; | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     openMoviePopup(id, event){ |     openMoviePopup(id, type, event){ | ||||||
|       eventHub.$emit('openMoviePopup', id, event); |       console.log('open:', id, type, event) | ||||||
|  |       eventHub.$emit('openMoviePopup', id, type, event); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
|     </div> |     </div> | ||||||
|     <ul class="nav__list"> |     <ul class="nav__list"> | ||||||
|       <li class="nav__item" v-for="item in listTypes" v-if="item.isCategory"> |       <li class="nav__item" v-for="item in listTypes" v-if="item.isCategory"> | ||||||
|         <router-link class="nav__link" :to="{name: 'home-category', params: {category: item.query}}"> |         <router-link class="nav__link" :to="{name: item.name, params: {mode: item.type, category: item.query}}"> | ||||||
|           <div class="nav__link-wrap"> |           <div class="nav__link-wrap"> | ||||||
|             <svg class="nav__link-icon"> |             <svg class="nav__link-icon"> | ||||||
|               <use :xlink:href="'#icon_' + item.query"></use> |               <use :xlink:href="'#icon_' + item.query"></use> | ||||||
| @@ -22,14 +22,22 @@ | |||||||
|         </router-link> |         </router-link> | ||||||
|       </li> |       </li> | ||||||
|       <li class="nav__item nav__item--profile"> |       <li class="nav__item nav__item--profile"> | ||||||
|         <div  class="nav__link nav__link--profile"  @click="requestToken" v-if="!userLoggedIn"> | <!--         <div  class="nav__link nav__link--profile"  @click="requestToken" v-if="!userLoggedIn"> | ||||||
|           <div class="nav__link-wrap"> |           <div class="nav__link-wrap"> | ||||||
|             <svg class="nav__link-icon"> |             <svg class="nav__link-icon"> | ||||||
|               <use xlink:href="#iconLogin"></use> |               <use xlink:href="#iconLogin"></use> | ||||||
|             </svg> |             </svg> | ||||||
|             <span class="nav__link-title">Log In</span> |             <span class="nav__link-title">Log In</span> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> --> | ||||||
|  |          <router-link  class="nav__link nav__link--profile" :to="{name: 'signin'}" v-if="!userLoggedIn"> | ||||||
|  |           <div class="nav__link-wrap"> | ||||||
|  |             <svg class="nav__link-icon"> | ||||||
|  |               <use xlink:href="#iconLogin"></use> | ||||||
|  |             </svg> | ||||||
|  |             <span class="nav__link-title">Sign in</span> | ||||||
|  |           </div> | ||||||
|  |         </router-link> | ||||||
|         <router-link  class="nav__link nav__link--profile" :to="{name: 'profile'}" v-if="userLoggedIn"> |         <router-link  class="nav__link nav__link--profile" :to="{name: 'profile'}" v-if="userLoggedIn"> | ||||||
|           <div class="nav__link-wrap"> |           <div class="nav__link-wrap"> | ||||||
|             <svg class="nav__link-icon"> |             <svg class="nav__link-icon"> | ||||||
| @@ -50,12 +58,12 @@ export default { | |||||||
|   data(){ |   data(){ | ||||||
|     return { |     return { | ||||||
|       listTypes: storage.listTypes, |       listTypes: storage.listTypes, | ||||||
|       userLoggedIn: storage.sessionId ? true : false |       userLoggedIn: localStorage.getItem('token') ? true : false | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     setUserStatus(){ |     setUserStatus(){ | ||||||
|       this.userLoggedIn = storage.sessionId ? true : false; |       this.userLoggedIn = localStorage.getItem('token') ? true : false; | ||||||
|     }, |     }, | ||||||
|     requestToken(){ |     requestToken(){ | ||||||
|       eventHub.$emit('requestToken'); |       eventHub.$emit('requestToken'); | ||||||
|   | |||||||
| @@ -1,17 +1,20 @@ | |||||||
| <template> | <template> | ||||||
|   <section class="profile"> |   <section class="profile"> | ||||||
|     <div class="profile__content" v-if="userLoggedIn === true"> |     <div class="profile__content" v-if="userLoggedIn"> | ||||||
|       <header class="profile__header"> |       <header class="profile__header"> | ||||||
|         <h2 class="profile__title">Hello {{ userName }}</h2> |         <h2 class="profile__title">{{ emoji }} Welcome {{ userName }}</h2> | ||||||
|         <button class="button" @click="logOut">Log Out</button> |         <button class="button" @click="logOut">Log Out</button> | ||||||
|       </header> |       </header> | ||||||
|       <movies-list :type="'component'" :mode="'favorite'"></movies-list> |       <movies-list v-for="item in listTypes" v-if="item.isProfileContent" :type="'component'" :mode="item.type" :category="item.query" :shortList="true"></movies-list> | ||||||
|  |       <!-- <movies-list v-for="item in listTypes" v-if="item.isCategory" :type="'component'" :mode="item.type" :shortList="true"></movies-list> --> | ||||||
|       <!-- <created-lists></created-lists> --> |       <!-- <created-lists></created-lists> --> | ||||||
|     </div> |     </div> | ||||||
|     <section class="not-found" v-if="userLoggedIn === false"> |     <section class="not-found" v-if="!userLoggedIn"> | ||||||
|       <div class="not-found__content"> |       <div class="not-found__content"> | ||||||
|         <h2 class="not-found__title">Authentication Request Failed</h2> |         <h2 class="not-found__title">Authentication Request Failed</h2> | ||||||
|         <button class="not-found__button button" @click="requestToken">Log In</button> |         <router-link :to="{name: 'signin'}" exact title="Sign in here"> | ||||||
|  |           <button class="not-found__button button">Sign In</button> | ||||||
|  |         </router-link> | ||||||
|       </div> |       </div> | ||||||
|     </section> |     </section> | ||||||
|   </section> |   </section> | ||||||
| @@ -28,25 +31,12 @@ export default { | |||||||
|   data(){ |   data(){ | ||||||
|     return{ |     return{ | ||||||
|       userLoggedIn: '', |       userLoggedIn: '', | ||||||
|       userName: '' |       userName: '', | ||||||
|  |       emoji: '', | ||||||
|  |       listTypes: storage.listTypes | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     requestPermission(){ |  | ||||||
|       let query = location.search.substring(1); |  | ||||||
|       if(query.length){ |  | ||||||
|         let params = query.split('&'); |  | ||||||
|         let token = params[0].split('=')[1]; |  | ||||||
|         let status = params[1].split('=')[0]; |  | ||||||
|         if(status == 'approved'){ |  | ||||||
|           this.createSession(token); |  | ||||||
|         } else { |  | ||||||
|           this.userLoggedIn = false; |  | ||||||
|         } |  | ||||||
|       } else { |  | ||||||
|         this.userLoggedIn = false; |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     createSession(token){ |     createSession(token){ | ||||||
|       axios.get(`https://api.themoviedb.org/3/authentication/session/new?api_key=${storage.apiKey}&request_token=${token}`) |       axios.get(`https://api.themoviedb.org/3/authentication/session/new?api_key=${storage.apiKey}&request_token=${token}`) | ||||||
|       .then(function(resp){ |       .then(function(resp){ | ||||||
| @@ -60,16 +50,14 @@ export default { | |||||||
|           } |           } | ||||||
|       }.bind(this)); |       }.bind(this)); | ||||||
|     }, |     }, | ||||||
|     getUserInfo(){ |     getNewEmoji(){ | ||||||
|       axios.get(`https://api.themoviedb.org/3/account?api_key=${storage.apiKey}&session_id=${storage.sessionId}`) |       axios.get(`https://api.kevinmidboe.com/api/v1/emoji`) | ||||||
|       .then(function(resp){ |       .then(function(resp){ | ||||||
|           let data = resp.data; |           this.emoji = resp.data.emoji; | ||||||
|           this.userName = data.username; |  | ||||||
|           if (!localStorage.getItem('user_id')) localStorage.setItem('user_id', data.id); |  | ||||||
|       }.bind(this)) |       }.bind(this)) | ||||||
|       .catch(function (error) { |     }, | ||||||
|         this.logOut(); |     getUserInfo(){ | ||||||
|       }.bind(this)); |       this.userName = localStorage.getItem('username');  | ||||||
|     }, |     }, | ||||||
|     requestToken(){ |     requestToken(){ | ||||||
|       eventHub.$emit('requestToken'); |       eventHub.$emit('requestToken'); | ||||||
| @@ -83,11 +71,12 @@ export default { | |||||||
|   created(){ |   created(){ | ||||||
|     document.title = 'Profile' + storage.pageTitlePostfix; |     document.title = 'Profile' + storage.pageTitlePostfix; | ||||||
|     storage.backTitle = document.title; |     storage.backTitle = document.title; | ||||||
|     if(!storage.sessionId){ |     if(!localStorage.getItem('token')){ | ||||||
|       this.requestPermission(); |       this.userLoggedIn = false; | ||||||
|     } else { |     } else { | ||||||
|       this.userLoggedIn = true; |       this.userLoggedIn = true; | ||||||
|       this.getUserInfo(); |       this.getUserInfo(); | ||||||
|  |       this.getNewEmoji(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -97,14 +86,6 @@ export default { | |||||||
| @import "./src/scss/variables"; | @import "./src/scss/variables"; | ||||||
| @import "./src/scss/media-queries"; | @import "./src/scss/media-queries"; | ||||||
| .profile{ | .profile{ | ||||||
|   &__content{ |  | ||||||
|     .wrapper{ |  | ||||||
|       min-height: calc(100vh - 175px); |  | ||||||
|       @include tablet-min{ |  | ||||||
|         min-height: calc(100vh - 171px); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   &__header{ |   &__header{ | ||||||
|     display: flex; |     display: flex; | ||||||
|     align-items: center; |     align-items: center; | ||||||
|   | |||||||
							
								
								
									
										267
									
								
								src/components/Register.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,267 @@ | |||||||
|  | <template> | ||||||
|  |   <section class="profile"> | ||||||
|  |     <div class="profile__content"> | ||||||
|  |       <header class="profile__header"> | ||||||
|  |         <h2 class="profile__title">Register new user</h2> | ||||||
|  |       </header> | ||||||
|  |  | ||||||
|  |       <form class="form"> | ||||||
|  |         <div class="form__buffer"></div> | ||||||
|  |  | ||||||
|  |         <div> | ||||||
|  |           <div class="form__group"> | ||||||
|  |             <svg class="form__group__input-icon"> | ||||||
|  |               <use xlink:href="#iconEmail"></use> | ||||||
|  |             </svg> | ||||||
|  |             <input class="form__group-input" type="username" ref="username" placeholder="Username" > | ||||||
|  |           </div> | ||||||
|  |           <div class="form__group"> | ||||||
|  |             <svg class="form__group__input-icon"> | ||||||
|  |               <use xlink:href="#iconKeyhole"></use> | ||||||
|  |             </svg> | ||||||
|  |             <input class="form__group-input" type="password" ref="password" placeholder="Password"> | ||||||
|  |           </div> | ||||||
|  |           <div class="form__group"> | ||||||
|  |             <svg class="form__group__input-icon"> | ||||||
|  |               <use xlink:href="#iconKeyhole"></use> | ||||||
|  |             </svg> | ||||||
|  |             <input class="form__group-input" type="password" ref="password_re" placeholder="Repeat password"> | ||||||
|  |           </div> | ||||||
|  |  | ||||||
|  |           <transition name="message-fade"> | ||||||
|  |             <div class="message" :class="messageClass" v-if="showMessage"> | ||||||
|  |               <span class="message-text">{{ messageText }}</span> | ||||||
|  |               <span class="message-dismiss" v-on:click="dismissMessage">X</span> | ||||||
|  |             </div> | ||||||
|  |           </transition> | ||||||
|  |  | ||||||
|  |           <div class="form__group"> | ||||||
|  |             <button type="button" class="button" v-on:click="requestNewUser">Register</button> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </form> | ||||||
|  |        | ||||||
|  |       <div class="form__group"> | ||||||
|  |         <router-link class="form__group-link" :to="{name: 'signin'}" exact title="Sign in here"> | ||||||
|  |           <span class="form__group-signin">Sign in here</span> | ||||||
|  |         </router-link> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <!-- <created-lists></created-lists> --> | ||||||
|  |     </div> | ||||||
|  |     <section class="not-found" v-if="userLoggedIn === false"> | ||||||
|  |       <div class="not-found__content"> | ||||||
|  |         <h2 class="not-found__title">Authentication Request Failed</h2> | ||||||
|  |         <button class="not-found__button button" @click="requestToken">Log In</button> | ||||||
|  |       </div> | ||||||
|  |     </section> | ||||||
|  |   </section> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | import axios from 'axios' | ||||||
|  | import storage from '../storage.js' | ||||||
|  | import MoviesList from './MoviesList.vue' | ||||||
|  | // import CreatedLists from './CreatedLists.vue' | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   components: { MoviesList }, | ||||||
|  |   data(){ | ||||||
|  |     return{ | ||||||
|  |       userLoggedIn: '', | ||||||
|  |       userName: '', | ||||||
|  |       showMessage: false, | ||||||
|  |       messageClass: 'message-success', | ||||||
|  |       messageText: 'hello world' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     requestNewUser(){ | ||||||
|  |       let username = this.$refs.username.value; | ||||||
|  |       let password = this.$refs.password.value; | ||||||
|  |       let password_re = this.$refs.password_re.value; | ||||||
|  |  | ||||||
|  |       let verifyCredentials = this.checkCredentials(username, password, password_re); | ||||||
|  |        | ||||||
|  |       if (verifyCredentials.verified) { | ||||||
|  |         axios.post(`https://api.kevinmidboe.com/api/v1/user`, { | ||||||
|  |           username: username, | ||||||
|  |           password: password | ||||||
|  |         }) | ||||||
|  |         .then(function(resp) { | ||||||
|  |           let data = resp.data; | ||||||
|  |           if (data.success){ | ||||||
|  |             this.msg(data.message, 'success'); | ||||||
|  |             localStorage.setItem('token', data.token); | ||||||
|  |             localStorage.setItem('username', username); | ||||||
|  |             localStorage.setItem('admin', data.admin) | ||||||
|  |              | ||||||
|  |             eventHub.$emit('setUserStatus'); | ||||||
|  |             this.$router.push({ name: 'profile' }) | ||||||
|  |           } | ||||||
|  |         }.bind(this)) | ||||||
|  |         .catch(function(error){ | ||||||
|  |           this.msg(error.response.data.error, 'warning') | ||||||
|  |         }.bind(this)); | ||||||
|  |       }  | ||||||
|  |       else { | ||||||
|  |         this.msg(verifyCredentials.reason, 'warning'); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     checkCredentials(username, password, password_re) { | ||||||
|  |       if (password !== password_re) { | ||||||
|  |         return { | ||||||
|  |           verified: false, | ||||||
|  |           reason: 'Passwords do not match' | ||||||
|  |         } | ||||||
|  |       }  | ||||||
|  |       else if (username === undefined) { | ||||||
|  |         return { | ||||||
|  |           verified: false, | ||||||
|  |           reason: 'Please insert username' | ||||||
|  |         } | ||||||
|  |       }  | ||||||
|  |       else { | ||||||
|  |         return { | ||||||
|  |           verified: true, | ||||||
|  |           reason: 'Verified credentials' | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     msg(text, status){ | ||||||
|  |       if (status === 'warning') | ||||||
|  |         this.messageClass = 'message-warning'; | ||||||
|  |       else if (status === 'success') | ||||||
|  |         this.messageClass = 'message-success'; | ||||||
|  |       else | ||||||
|  |         this.messageClass = 'message-info'; | ||||||
|  |       this.messageText = text; | ||||||
|  |       this.showMessage = true; | ||||||
|  |       // setTimeout(() => this.showMessage = false, 3500); | ||||||
|  |     }, | ||||||
|  |     dismissMessage(){ | ||||||
|  |       this.showMessage = false; | ||||||
|  |     }, | ||||||
|  |     logOut(){ | ||||||
|  |       localStorage.clear(); | ||||||
|  |       eventHub.$emit('setUserStatus'); | ||||||
|  |       this.$router.push({ name: 'home' }); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   created(){ | ||||||
|  |     document.title = 'Profile' + storage.pageTitlePostfix; | ||||||
|  |     storage.backTitle = document.title; | ||||||
|  |   }, | ||||||
|  |   mounted(){ | ||||||
|  |     // this.$refs.email.focus(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style lang="scss"> | ||||||
|  | @import "./src/scss/variables"; | ||||||
|  | @import "./src/scss/media-queries"; | ||||||
|  | .message-enter-active { | ||||||
|  |   transition: all .3s ease; | ||||||
|  | } | ||||||
|  | .message-fade-leave-active { | ||||||
|  |   transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0); | ||||||
|  | } | ||||||
|  | .message-fade-enter, .message-fade-leave-to { | ||||||
|  |   opacity: 0; | ||||||
|  | } | ||||||
|  | .message{ | ||||||
|  |   width: 75%; | ||||||
|  |   max-width: 35rem; | ||||||
|  |   margin: 0 auto; | ||||||
|  |   margin-bottom: 1rem; | ||||||
|  |   padding: 12px 15px 12px 15px; | ||||||
|  |   position: relative; | ||||||
|  |   &-text{ | ||||||
|  |     font-weight: 300; | ||||||
|  |   } | ||||||
|  |   &-dismiss{ | ||||||
|  |     position: absolute; | ||||||
|  |     font-size: 17px; | ||||||
|  |     font-weight: 100; | ||||||
|  |     top: 0; | ||||||
|  |     right: 0; | ||||||
|  |     margin-top: 2px; | ||||||
|  |     margin-right: 5px; | ||||||
|  |     cursor: pointer; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .message-warning{ | ||||||
|  |   background-color: #f2dede; | ||||||
|  |   border: 1px solid #b75b91; | ||||||
|  |   color: #b75b91; | ||||||
|  | } | ||||||
|  | .message-success{ | ||||||
|  |   background-color: #dff0d9; | ||||||
|  |   border: 1px solid #3e7549; | ||||||
|  |   color: #3e7549; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .form{ | ||||||
|  |   z-index: 15; | ||||||
|  |   background-color: $c-light; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   @include tablet-min{ | ||||||
|  |   } | ||||||
|  |   &__buffer{ | ||||||
|  |     width: 100%; | ||||||
|  |     height: 4rem; | ||||||
|  |   } | ||||||
|  |   &__group{ | ||||||
|  |     display: flex; | ||||||
|  |     justify-content: center; | ||||||
|  |     @include tablet-min{ | ||||||
|  |     } | ||||||
|  |     &-input{ | ||||||
|  |       width: 75%; | ||||||
|  |       max-width: 35rem; | ||||||
|  |       padding: 15px 10px 15px 45px; | ||||||
|  |       outline: none; | ||||||
|  |       background-color: $c-white; | ||||||
|  |       color: $c-dark; | ||||||
|  |       font-weight: 100; | ||||||
|  |       font-size: 20px; | ||||||
|  |       border: 1px solid $c-dark; | ||||||
|  |       margin-bottom: 1rem; | ||||||
|  |       margin-left: -2.2rem; | ||||||
|  |       z-index: 3; | ||||||
|  |       &:focus, &:hover { | ||||||
|  |         border-color: $c-dark; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     &-input[type="username"] { | ||||||
|  |       margin-bottom: 3rem; | ||||||
|  |     } | ||||||
|  |     &__input-icon{ | ||||||
|  |       width: 24px; | ||||||
|  |       height: 24px; | ||||||
|  |       fill: rgba($c-dark, 0.5); | ||||||
|  |       transition: fill 0.5s ease; | ||||||
|  |       pointer-events: none; | ||||||
|  |       margin-top: 15px; | ||||||
|  |       margin-left: 15px; | ||||||
|  |       z-index: 8; | ||||||
|  |     } | ||||||
|  |     &-link{ | ||||||
|  |       text-decoration: none; | ||||||
|  |       color: black; | ||||||
|  |       margin-top: 1rem; | ||||||
|  |     } | ||||||
|  |     &-signin{ | ||||||
|  |       text-transform: uppercase; | ||||||
|  |       font-weight: 300; | ||||||
|  |       font-size: 11px; | ||||||
|  |       line-height: 2; | ||||||
|  |       letter-spacing: 0.5px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | </style> | ||||||
							
								
								
									
										122
									
								
								src/components/Signin.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,122 @@ | |||||||
|  | <template> | ||||||
|  |   <section class="profile"> | ||||||
|  |     <div class="profile__content"> | ||||||
|  |       <header class="profile__header"> | ||||||
|  |         <h2 class="profile__title">Register new user</h2> | ||||||
|  |       </header> | ||||||
|  |  | ||||||
|  |       <form class="form"> | ||||||
|  |         <div class="form__buffer"></div> | ||||||
|  |         <div> | ||||||
|  |           <div class="form__group"> | ||||||
|  |             <svg class="form__group__input-icon"> | ||||||
|  |               <use xlink:href="#iconEmail"></use> | ||||||
|  |             </svg> | ||||||
|  |             <input class="form__group-input" type="username" ref="username" placeholder="Username" > | ||||||
|  |           </div> | ||||||
|  |           <div class="form__group"> | ||||||
|  |             <svg class="form__group__input-icon"> | ||||||
|  |               <use xlink:href="#iconKeyhole"></use> | ||||||
|  |             </svg> | ||||||
|  |             <input class="form__group-input" type="password" ref="password" placeholder="Password"> | ||||||
|  |           </div> | ||||||
|  |            | ||||||
|  |           <transition name="message-fade"> | ||||||
|  |               <div class="message" :class="messageClass" v-if="showMessage"> | ||||||
|  |                 <span class="message-text">{{ messageText }}</span> | ||||||
|  |                 <span class="message-dismiss" @click="showMessage=false">X</span> | ||||||
|  |               </div> | ||||||
|  |             </transition> | ||||||
|  |            | ||||||
|  |           <div class="form__group"> | ||||||
|  |             <button type="button" class="button" v-on:click="signin">Sign in</button> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </form> | ||||||
|  |        | ||||||
|  |       <div class="form__group"> | ||||||
|  |         <router-link class="form__group-link" :to="{name: 'register'}" exact title="Sign in here"> | ||||||
|  |           <span class="form__group-signin">Don't have a user? Register here</span> | ||||||
|  |         </router-link> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <!-- <created-lists></created-lists> --> | ||||||
|  |     </div> | ||||||
|  |   </section> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | import axios from 'axios' | ||||||
|  | import storage from '../storage.js' | ||||||
|  | import MoviesList from './MoviesList.vue' | ||||||
|  | // import CreatedLists from './CreatedLists.vue' | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   components: { MoviesList }, | ||||||
|  |   data(){ | ||||||
|  |     return{ | ||||||
|  |       userLoggedIn: '', | ||||||
|  |       userName: '', | ||||||
|  |       showMessage: false, | ||||||
|  |       messageClass: 'message-success', | ||||||
|  |       messageText: 'hello world' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     signin(){ | ||||||
|  |       let username = this.$refs.username.value; | ||||||
|  |       let password = this.$refs.password.value; | ||||||
|  |  | ||||||
|  |       axios.post(`https://api.kevinmidboe.com/api/v1/user/login`, { | ||||||
|  |         username: username, | ||||||
|  |         password: password | ||||||
|  |       }) | ||||||
|  |       .then(function (resp){ | ||||||
|  |         let data = resp.data; | ||||||
|  |         if (data.success){ | ||||||
|  |           localStorage.setItem('token', data.token); | ||||||
|  |           localStorage.setItem('username', username); | ||||||
|  |           localStorage.setItem('admin', data.admin); | ||||||
|  |           this.userLoggedIn = true; | ||||||
|  |            | ||||||
|  |           eventHub.$emit('setUserStatus'); | ||||||
|  |           this.$router.push({ name: 'profile' }) | ||||||
|  |         } | ||||||
|  |       }.bind(this)) | ||||||
|  |       .catch(function (error){ | ||||||
|  |         if (error.message.endsWith('401')) | ||||||
|  |           this.msg('Incorrect username or password ', 'warning') | ||||||
|  |         else | ||||||
|  |           this.msg(error.message, 'warning') | ||||||
|  |       }.bind(this)); | ||||||
|  |     }, | ||||||
|  |     msg(text, status){ | ||||||
|  |       if (status === 'warning') | ||||||
|  |         this.messageClass = 'message-warning'; | ||||||
|  |       else if (status === 'success') | ||||||
|  |         this.messageClass = 'message-success'; | ||||||
|  |       else | ||||||
|  |         this.messageClass = 'message-info'; | ||||||
|  |       this.messageText = text; | ||||||
|  |       this.showMessage = true; | ||||||
|  |       // setTimeout(() => this.showMessage = false, 3500); | ||||||
|  |     }, | ||||||
|  |     toggleView(){ | ||||||
|  |       this.register = false; | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   created(){ | ||||||
|  |     document.title = 'Sign in' + storage.pageTitlePostfix; | ||||||
|  |     storage.backTitle = document.title; | ||||||
|  |     if (this.userLoggedIn == true) { | ||||||
|  |       this.$router.push({ name: 'profile' }) | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   mounted(){ | ||||||
|  |     // this.$refs.email.focus(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style lang="scss"> | ||||||
|  | </style> | ||||||
							
								
								
									
										148
									
								
								src/components/TorrentList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,148 @@ | |||||||
|  | <template> | ||||||
|  |   <section> | ||||||
|  |     <div v-if="listLoaded"> | ||||||
|  |       <div v-if="torrents.length"> | ||||||
|  |         <data-tablee | ||||||
|  |       :rows="torrents" | ||||||
|  |       :cols="cols" | ||||||
|  |       empty="-" | ||||||
|  |     > | ||||||
|  |       <span | ||||||
|  |         class="data-tablee-icon" | ||||||
|  |         slot="sort-icon" | ||||||
|  |         slot-scope="{ sortment, sorted, arrow }" | ||||||
|  |       > | ||||||
|  |         {{ sorted ? arrow + ' ' + (sortment === 'ascending' ? 'ASC' : 'DESC') : '' }} | ||||||
|  |       </span> | ||||||
|  |  | ||||||
|  |       <template | ||||||
|  |         slot="row" | ||||||
|  |         slot-scope="{ row, index }" | ||||||
|  |       > | ||||||
|  |         <td class="data-tablee-cell -content data-tablee-text" v-bind:title="row.name" v-if="!renderName">{{ row.name.slice(0, 50) }}</td> | ||||||
|  |         <td class="data-tablee-cell -content data-tablee-text" v-on:click="showInfo(row.name)">{{ row.seed }}</td> | ||||||
|  |         <td class="data-tablee-cell -content data-tablee-text" v-on:click="showInfo(row.name)">{{ row.size }}</td> | ||||||
|  |         <td class="data-tablee-cell -content data-tablee-text magnet"> | ||||||
|  |           <button type='button' class="button" @click="sendTorrent(row.magnet)">Add</button> | ||||||
|  |         </td> | ||||||
|  |       </template> | ||||||
|  |     </data-tablee> | ||||||
|  |       </div> | ||||||
|  |       <section v-if="!torrents.length" class=""> | ||||||
|  |         <div class="not-found__content"> | ||||||
|  |           <h2 class="not-found__title">{{ errorMessage }}</h2> | ||||||
|  |         </div> | ||||||
|  |       </section> | ||||||
|  |     </div> | ||||||
|  |     <i v-if="!listLoaded" class="torrentloader"></i> | ||||||
|  |   </section> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | import axios from 'axios' | ||||||
|  | import numeral from 'numeral' | ||||||
|  | import storage from '../storage.js' | ||||||
|  |  | ||||||
|  | // import testTorrents from './torrents.json'; | ||||||
|  |  | ||||||
|  | let tablet = window.innerWidth < 768 ? true : false; | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   props: ['query'], | ||||||
|  |   beforeRouteLeave (to, from, next) { | ||||||
|  |     if(from.name == 'search'){ | ||||||
|  |       eventHub.$emit('setSearchQuery', true); | ||||||
|  |     } | ||||||
|  |     next(); | ||||||
|  |   }, | ||||||
|  |   data() { | ||||||
|  |     return { | ||||||
|  |       torrents: [], | ||||||
|  |       listLoaded: false, | ||||||
|  |       errorMessage: '', | ||||||
|  |       renderName: tablet, | ||||||
|  |       cols: [ | ||||||
|  |         { label: 'Name', field: 'name', sort: true, hidden: tablet }, | ||||||
|  |         { label: 'Seeders', field: 'seed', sort: true }, | ||||||
|  |         { label: 'Size', field: 'size', sort: (a, b) => this.sortableSize(a) - this.sortableSize(b) }, | ||||||
|  |         { label: 'Add', align: 'center' } | ||||||
|  |       ], | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     fetchTorrents(){ | ||||||
|  |       axios.get(`https://api.kevinmidboe.com/api/v1/pirate/search?query=${this.query}&filter=all&page=${this.currentPage}`, { | ||||||
|  |         headers: {authorization: storage.token}, | ||||||
|  |       }) | ||||||
|  |       .then(resp => { | ||||||
|  |           let data = resp.data; | ||||||
|  |           this.torrents = data.results; | ||||||
|  |           this.listLoaded = true; | ||||||
|  |       }) | ||||||
|  |       .catch(e => { | ||||||
|  |         const error = e.toString() | ||||||
|  |         this.errorMessage = error.indexOf('401') != -1 ? 'Permission denied' : 'Nothing found'; | ||||||
|  |         this.listLoaded = true; | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  |     sendTorrent(magnet){ | ||||||
|  |       axios.post(`https://api.kevinmidboe.com/api/v1/pirate/add`, { | ||||||
|  |         magnet: magnet }, { headers: {authorization: storage.token} | ||||||
|  |       }) | ||||||
|  |       .catch((resp) => { console.log('error:', resp.data) }) | ||||||
|  |       .then((resp) => { console.log('addTorrent resp: ', resp) }) | ||||||
|  |     }, | ||||||
|  |     showInfo(text){ | ||||||
|  |       alert(text) | ||||||
|  |     }, | ||||||
|  |     sortableSize(string) { | ||||||
|  |        const UNITS = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; | ||||||
|  |        const [numStr, unit] = string.split(' '); | ||||||
|  |        if (UNITS.indexOf(unit) === -1) | ||||||
|  |           return string | ||||||
|  |        const exponent = UNITS.indexOf(unit) * 3 | ||||||
|  |        return numStr * (Math.pow(10, exponent)) | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   created(){ | ||||||
|  |     this.fetchTorrents(); | ||||||
|  |   }, | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style src="../scss/vue-data-tablee.css"></style> | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | @import "./src/scss/variables"; | ||||||
|  | @import "./src/scss/media-queries"; | ||||||
|  | .magnet{ | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | .add{ | ||||||
|  |   padding: 3px 15px 3px 15px; | ||||||
|  |   &:hover, &:active{ | ||||||
|  |     background: $c-dark; | ||||||
|  |     color: $c-white; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | .torrentloader{ | ||||||
|  |   animation: load 1s linear infinite; | ||||||
|  |   border: 2px solid $c-dark; | ||||||
|  |   border-radius: 50%; | ||||||
|  |   display: block; | ||||||
|  |   height: 30px; | ||||||
|  |   left: 50%; | ||||||
|  |   margin: 0 auto; | ||||||
|  |   width: 30px; | ||||||
|  |   &:after { | ||||||
|  |     border: 5px solid $c-green; | ||||||
|  |     border-radius: 50%; | ||||||
|  |     content: ''; | ||||||
|  |     left: 10px; | ||||||
|  |     position: absolute; | ||||||
|  |     top: 16px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @keyframes load { | ||||||
|  |   100% { transform: rotate(360deg); } | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										1895
									
								
								src/components/torrents.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -2,13 +2,14 @@ import Vue from 'vue' | |||||||
| import VueRouter from 'vue-router' | import VueRouter from 'vue-router' | ||||||
| import axios from 'axios' | import axios from 'axios' | ||||||
| import router from './routes' | import router from './routes' | ||||||
|  | import DataTablee from 'vue-data-tablee' | ||||||
|  |  | ||||||
| import App from './App.vue' | import App from './App.vue' | ||||||
|  |  | ||||||
| window.eventHub = new Vue(); | window.eventHub = new Vue(); | ||||||
|  |  | ||||||
|  |  | ||||||
| Vue.use(VueRouter, axios) | Vue.use(VueRouter, axios) | ||||||
|  | Vue.use(DataTablee) | ||||||
|  |  | ||||||
| new Vue({ | new Vue({ | ||||||
|   el: '#app', |   el: '#app', | ||||||
|   | |||||||
| @@ -10,11 +10,18 @@ let routes = [ | |||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     name: 'home-category', |     name: 'home-category', | ||||||
|     path: '/movies/:category', |     path: '/list/:category', | ||||||
|     components: { |     components: { | ||||||
|       'list-router-view': require('./components/MoviesList.vue') |       'list-router-view': require('./components/MoviesList.vue') | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|  |   { | ||||||
|  |     name: 'request', | ||||||
|  |     path: '/request/all', | ||||||
|  |     components: { | ||||||
|  |       'request-router-view': require('./components/MoviesList.vue') | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|   { |   { | ||||||
|     name: 'search', |     name: 'search', | ||||||
|     path: '/search/:query', |     path: '/search/:query', | ||||||
| @@ -22,6 +29,13 @@ let routes = [ | |||||||
|       'search-router-view': require('./components/MoviesList.vue') |       'search-router-view': require('./components/MoviesList.vue') | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|  |   { | ||||||
|  |     name: 'user-requests', | ||||||
|  |     path: '/profile/requests', | ||||||
|  |     components: { | ||||||
|  |       'user-requests-router-view': require('./components/MoviesList.vue') | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|   { |   { | ||||||
|     name: 'movie', |     name: 'movie', | ||||||
|     path: '/movie/:id', |     path: '/movie/:id', | ||||||
| @@ -30,12 +44,40 @@ let routes = [ | |||||||
|     }, |     }, | ||||||
|     beforeEnter: (to, from, next) => { |     beforeEnter: (to, from, next) => { | ||||||
|       if(history.state && history.state.popup && from.name){ |       if(history.state && history.state.popup && from.name){ | ||||||
|         eventHub.$emit('openMoviePopup', to.params.id, false); |         eventHub.$emit('openMoviePopup', to.params.id, 'movie', false); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|       next(); |       next(); | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|  |   { | ||||||
|  |     name: 'show', | ||||||
|  |     path: '/show/:id', | ||||||
|  |     components: { | ||||||
|  |       'page-router-view': require('./components/MoviePage.vue') | ||||||
|  |     }, | ||||||
|  |     beforeEnter: (to, from, next) => { | ||||||
|  |       if(history.state && history.state.popup && from.name){ | ||||||
|  |         eventHub.$emit('openMoviePopup', to.params.id, 'show', false); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       next(); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: 'register', | ||||||
|  |     path: '/register', | ||||||
|  |     components: { | ||||||
|  |       'search-router-view': require('./components/Register.vue') | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: 'signin', | ||||||
|  |     path: '/signin', | ||||||
|  |     components: { | ||||||
|  |       'search-router-view': require('./components/Signin.vue') | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|   { |   { | ||||||
|     name: 'profile', |     name: 'profile', | ||||||
|     path: '/profile', |     path: '/profile', | ||||||
| @@ -52,12 +94,13 @@ let routes = [ | |||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     path: '*', |     path: '*', | ||||||
|     redirect: '/404' |     redirect: '/' | ||||||
|   } |   } | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| const router =  new VueRouter({ | const router =  new VueRouter({ | ||||||
|   mode: 'history', |   mode: 'history', | ||||||
|  |   base: '/request', | ||||||
|   routes, |   routes, | ||||||
|   linkActiveClass: 'is-active' |   linkActiveClass: 'is-active' | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -3,3 +3,7 @@ $c-green: #01d277; | |||||||
| $c-dark: #081c24; | $c-dark: #081c24; | ||||||
| $c-white: #ffffff; | $c-white: #ffffff; | ||||||
| $c-light: #f8f8f8; | $c-light: #f8f8f8; | ||||||
|  | $c-green-light: #dff0d9; | ||||||
|  | $c-green-dark: #3e7549; | ||||||
|  | $c-red-light: #f2dede; | ||||||
|  | $c-red-dark: #b75b91; | ||||||
|   | |||||||
							
								
								
									
										68
									
								
								src/scss/vue-data-tablee.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,68 @@ | |||||||
|  | .data-tablee { | ||||||
|  |   overflow: hidden; | ||||||
|  |   border: 1px solid #eaedef; | ||||||
|  |   width: 100%; | ||||||
|  |   border-radius: 5px; | ||||||
|  |   border-spacing: 0; } | ||||||
|  |  | ||||||
|  | .data-tablee-cell { | ||||||
|  |   position: relative; | ||||||
|  |   min-height: calc(27px + 4px); | ||||||
|  |   padding: 10px; | ||||||
|  |   border-top: 1px solid #eaedef; } | ||||||
|  |   .data-tablee-row:first-child > .data-tablee-cell { | ||||||
|  |     border-top: 0; } | ||||||
|  |   .data-tablee-cell::before { | ||||||
|  |     position: absolute; | ||||||
|  |     left: 0; | ||||||
|  |     top: 50%; | ||||||
|  |     display: block; | ||||||
|  |     width: 1px; | ||||||
|  |     height: 27px; | ||||||
|  |     background-color: #eaedef; | ||||||
|  |     transform: translateY(-50%); | ||||||
|  |     content: ''; } | ||||||
|  |   .data-tablee-cell:first-child::before { | ||||||
|  |     content: none; } | ||||||
|  |   .data-tablee-cell.-right { | ||||||
|  |     text-align: right; } | ||||||
|  |   .data-tablee-cell.-left { | ||||||
|  |     text-align: left; } | ||||||
|  |   .data-tablee-cell.-center { | ||||||
|  |     text-align: center; } | ||||||
|  |   .data-tablee-cell.-clickable { | ||||||
|  |     cursor: pointer; } | ||||||
|  |  | ||||||
|  | .data-tablee-text { | ||||||
|  |   font-size: 13px; | ||||||
|  |   font-weight: 400; | ||||||
|  |   color: #5e6684; } | ||||||
|  |  | ||||||
|  | .data-tablee-cell.-header { | ||||||
|  |   background-color: #fdfdfd; } | ||||||
|  |   .data-tablee-cell.-header > .data-tablee-text, | ||||||
|  |   .data-tablee-cell.-header > .data-tablee-icon { | ||||||
|  |     display: inline-block; | ||||||
|  |     font-size: 12px; | ||||||
|  |     font-weight: 400; | ||||||
|  |     text-transform: uppercase; | ||||||
|  |     color: #bec0d3; } | ||||||
|  |   .data-tablee-cell.-header > .data-tablee-icon { | ||||||
|  |     opacity: 0; | ||||||
|  |     transition: opacity .3s ease, transform .3s ease; } | ||||||
|  |   .data-tablee-cell.-header.-sortable { | ||||||
|  |     cursor: pointer; } | ||||||
|  |     .data-tablee-cell.-header.-sortable > .data-tablee-icon { | ||||||
|  |       opacity: .2; } | ||||||
|  |     .data-tablee-cell.-header.-sortable:hover > .data-tablee-icon { | ||||||
|  |       opacity: .8; } | ||||||
|  |     .data-tablee-cell.-header.-sortable:active > .data-tablee-icon { | ||||||
|  |       transition: transform .1s ease; | ||||||
|  |       transform: scale(1.5); } | ||||||
|  |     .data-tablee-cell.-header.-sortable.-right { | ||||||
|  |       padding-right: 6px; } | ||||||
|  |   .data-tablee-cell.-header.-sorting > .data-tablee-icon { | ||||||
|  |     opacity: 1; } | ||||||
|  |  | ||||||
|  | .data-tablee-text { | ||||||
|  |   line-height: 1; } | ||||||
| @@ -1,34 +1,40 @@ | |||||||
| let storage = { | let storage = { | ||||||
|   apiKey: 'a70dbfe19b800809dfdd3e89e8532c9e', |   apiKey: 'a70dbfe19b800809dfdd3e89e8532c9e', | ||||||
|   sessionId: localStorage.getItem('session_id') || null, |   token: localStorage.getItem('token') || null, | ||||||
|   userId: localStorage.getItem('user_id') || null, |   username: localStorage.getItem('username') || null, | ||||||
|  |   admin: localStorage.getItem('admin') || null, | ||||||
|   pageTitlePostfix: ' — ' + document.title, |   pageTitlePostfix: ' — ' + document.title, | ||||||
|   listTypes: [ |   listTypes: [ | ||||||
|     { |  | ||||||
|       title: 'Popular Movies', |  | ||||||
|       shortTitle: 'Popular', |  | ||||||
|       query: 'popular', |  | ||||||
|       type: 'collection', |  | ||||||
|       isCategory: true |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       title: 'Top Rated Movies', |  | ||||||
|       shortTitle: 'Top Rated', |  | ||||||
|       query: 'top_rated', |  | ||||||
|       type: 'collection', |  | ||||||
|       isCategory: true |  | ||||||
|     }, |  | ||||||
|     { |     { | ||||||
|       title: 'Upcoming Movies', |       title: 'Upcoming Movies', | ||||||
|       shortTitle: 'Upcoming', |       shortTitle: 'Upcoming', | ||||||
|       query: 'upcoming', |       query: 'upcoming', | ||||||
|  |       name: 'home-category', | ||||||
|       type: 'collection', |       type: 'collection', | ||||||
|       isCategory: true |       isCategory: true | ||||||
|     }, |     }, | ||||||
|  |     { | ||||||
|  |       title: 'Requested Movies & Shows', | ||||||
|  |       shortTitle: 'Requested', | ||||||
|  |       query: 'requests', | ||||||
|  |       name: 'home-category', | ||||||
|  |       type: 'requests', // Maybe change to separate group | ||||||
|  |       isCategory: true, | ||||||
|  |       isProfileContent: true | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|       title: 'Now Playing Movies', |       title: 'Now Playing Movies', | ||||||
|       shortTitle: 'Now Playing', |       shortTitle: 'Now Playing', | ||||||
|       query: 'now_playing', |       query: 'nowplaying', | ||||||
|  |       name: 'home-category', | ||||||
|  |       type: 'collection', | ||||||
|  |       isCategory: true | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: 'Popular Movies', | ||||||
|  |       shortTitle: 'Popular', | ||||||
|  |       query: 'popular', | ||||||
|  |       name: 'home-category', | ||||||
|       type: 'collection', |       type: 'collection', | ||||||
|       isCategory: true |       isCategory: true | ||||||
|     }, |     }, | ||||||
| @@ -41,6 +47,15 @@ let storage = { | |||||||
|       title: 'Your Favorite Movies', |       title: 'Your Favorite Movies', | ||||||
|       query: 'favorite', |       query: 'favorite', | ||||||
|       isCategory: false |       isCategory: false | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       title: 'Your Requests', | ||||||
|  |       shortTitle: 'User Requests', | ||||||
|  |       query: 'user-requests', | ||||||
|  |       name: 'user-requests', | ||||||
|  |       type: 'user-requests', | ||||||
|  |       isProfileContent: true | ||||||
|  |       // isCategory: true, | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|   categories: {}, |   categories: {}, | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ module.exports = { | |||||||
|   entry: './src/main.js', |   entry: './src/main.js', | ||||||
|   output: { |   output: { | ||||||
|     path: path.resolve(__dirname, './dist'), |     path: path.resolve(__dirname, './dist'), | ||||||
|     publicPath: '/dist/', |     publicPath: '/request/dist/', | ||||||
|     filename: 'build.js' |     filename: 'build.js' | ||||||
|   }, |   }, | ||||||
|   module: { |   module: { | ||||||
| @@ -29,7 +29,8 @@ module.exports = { | |||||||
|         test: /\.(png|jpg|gif|svg)$/, |         test: /\.(png|jpg|gif|svg)$/, | ||||||
|         loader: 'file-loader', |         loader: 'file-loader', | ||||||
|         options: { |         options: { | ||||||
|           name: '[name].[ext]?[hash]' |           name: '[name].[ext]' | ||||||
|  |           // name: '[name].[ext]?[hash]' | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     ] |     ] | ||||||
| @@ -49,7 +50,7 @@ module.exports = { | |||||||
|   performance: { |   performance: { | ||||||
|     hints: false |     hints: false | ||||||
|   }, |   }, | ||||||
|   devtool: '#eval-source-map' |   // devtool: '#eval-source-map' | ||||||
| } | } | ||||||
|  |  | ||||||
| if (process.env.NODE_ENV === 'production') { | if (process.env.NODE_ENV === 'production') { | ||||||
|   | |||||||