├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── composer.json ├── readme.md └── src ├── .env.example ├── .env.travis ├── PodcastServiceProvider.php ├── app ├── Console │ ├── Commands │ │ └── UpdatePodcastItems.php │ └── Kernel.php ├── Http │ └── Controllers │ │ ├── PodcastItemsController.php │ │ └── PodcastsController.php └── Models │ ├── Podcast.php │ ├── PodcastItem.php │ └── User.php ├── database └── migrations │ ├── 2017_01_08_055955_create_podcasts_table.php │ └── 2017_01_08_060244_create_podcast_items_table.php ├── public └── css │ ├── app.css │ ├── app.css.map │ └── icons │ ├── ic_add_black_36dp.png │ ├── ic_add_white_36dp.png │ ├── ic_clear_black_36dp.png │ ├── ic_clear_white_36dp.png │ ├── ic_delete_black_24dp.png │ ├── ic_delete_white_24dp.png │ ├── ic_done_all_black_36dp.png │ ├── ic_done_all_white_36dp.png │ ├── ic_done_black_36dp.png │ ├── ic_done_white_36dp.png │ ├── ic_event_black_36dp.png │ ├── ic_event_white_36dp.png │ ├── ic_favorite_black_36dp.png │ ├── ic_favorite_border_black_36dp.png │ ├── ic_favorite_grey600_36dp.png │ ├── ic_favorite_white_36dp.png │ ├── ic_file_download_black_36dp.png │ ├── ic_file_download_white_36dp.png │ ├── ic_play_arrow_black_36dp.png │ ├── ic_play_arrow_white_36dp.png │ ├── ic_play_circle_filled_black_36dp.png │ ├── ic_play_circle_filled_white_36dp.png │ ├── ic_play_circle_outline_black_36dp.png │ ├── ic_play_circle_outline_white_36dp.png │ ├── ic_search_black_36dp.png │ ├── ic_search_white_36dp.png │ └── play.png ├── resources ├── assets │ └── sass │ │ ├── _variables.scss │ │ ├── app.scss │ │ └── partials │ │ ├── _base.scss │ │ ├── _buttons.scss │ │ ├── _mixins.scss │ │ ├── _modals.scss │ │ ├── _pagination.scss │ │ ├── _panels.scss │ │ ├── _player.scss │ │ └── _podcast.scss └── views │ ├── layouts │ └── app.blade.php │ ├── modals │ ├── modal-add.blade.php │ ├── modal-delete.blade.php │ ├── modal-markAllRead.blade.php │ └── modal-markRead.blade.php │ ├── partials │ ├── form-status.blade.php │ └── nav.blade.php │ ├── podcasts │ ├── favorites.blade.php │ ├── item.blade.php │ ├── list.blade.php │ ├── manage.blade.php │ ├── player.blade.php │ ├── searchresults.blade.php │ └── settings.blade.php │ └── scripts │ ├── add-modal-script.blade.php │ ├── delete-modal-script.blade.php │ └── podcast-scripts.blade.php └── routes └── web.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [jeremykenedy] 4 | patreon: jeremykenedy 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### OSX BOOGERS ### 2 | .DS_Store 3 | *._DS_Store 4 | ._.DS_Store 5 | *._ 6 | ._* 7 | ._.* 8 | 9 | ### WINDOWS BOOGERS ### 10 | Thumbs.db 11 | 12 | ### Sass ### 13 | /.sass-cache/* 14 | .sass-cache 15 | 16 | ### SUBLIMETEXT BOOGERS ### 17 | *.sublime-workspace 18 | 19 | ### PHPSTORM BOOGERS ### 20 | .idea/* 21 | /.idea/* 22 | 23 | ### DIFFERENT TYPE OF MASTER CONFIGS ### 24 | composer.lock 25 | 26 | ### ASSET EXCLUSIONS ### 27 | vendor/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - '5.6' 5 | - '7.0' 6 | - '7.1' 7 | - hhvm 8 | - nightly 9 | 10 | services: 11 | - mysql 12 | 13 | before_script: 14 | - cp .env.travis .env 15 | - mysql -u root -e 'create database laravelPodcast;' 16 | - composer self-update 17 | - composer install --dev --prefer-source --no-interaction 18 | - php artisan key:generate 19 | - php artisan migrate:install --env=testing --no-interaction 20 | - composer dump-autoload 21 | - sudo chgrp -R www-data storage bootstrap/cache 22 | - sudo chmod -R ug+rwx storage bootstrap/cache 23 | - sudo php artisan config:cache -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jeremykenedy/laravelpodcast", 3 | "description": "Laravel podcast manager package", 4 | "keywords": ["laravel", "podcast"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "jeremykenedy", 9 | "email": "jeremykenedy@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=5.6.4", 14 | "laravelcollective/html": "^5.3.0", 15 | "willvincent/feeds": "^1.1", 16 | "intervention/image": "^2.3" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "jeremykenedy\\laravelpodcast\\": "src/" 21 | } 22 | }, 23 | "extra": { 24 | "branch-alias": { 25 | "dev-master": "0.0.1-dev" 26 | } 27 | }, 28 | "minimum-stability": "dev", 29 | "prefer-stable": true 30 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # laravelpodcast | A Laravel podcast manager package - v0.0.8 2 | 3 | [![Total Downloads](https://poser.pugx.org/jeremykenedy/laravelpodcast/d/total.svg)](https://packagist.org/packages/jeremykenedy/laravelpodcast) 4 | [![StyleCI](https://github.styleci.io/repos/81772813/shield?branch=master)](https://github.styleci.io/repos/81772813) 5 | [![Latest Stable Version](https://poser.pugx.org/jeremykenedy/laravelpodcast/v/stable.svg)](https://packagist.org/packages/jeremykenedy/laravelpodcast) 6 | [![License](https://poser.pugx.org/jeremykenedy/laravelpodcast/license.svg)](https://packagist.org/packages/jeremykenedy/laravelpodcast) 7 | 8 | ## Introduction 9 | 10 | Laravel Podcast Manager is a complete podcast manager package for Laravel 5.3+ that enables you to manage RSS feeds for your favorite podcasts and listen to the episodes in a seamless UI. 11 | 12 | ## Requirements 13 | 14 | * [Laravel 5.3+](https://laravel.com/docs/installation) 15 | 16 | Example new project creation command: 17 | 18 | ```laravel new podcast``` 19 | 20 | * [Laravel Authentication Scaffolding](https://laravel.com/docs/authentication) 21 | 22 | Authentication installation command: 23 | 24 | ```php artisan make:auth``` 25 | 26 | ## Installation 27 | 28 | 1. From your projects root folder in terminal run: 29 | 30 | ``` 31 | composer require jeremykenedy/laravelpodcast 32 | ``` 33 | 34 | 2. Register the package with laravel in `config/app.php` under `providers` with the following: 35 | 36 | ``` 37 | Collective\Html\HtmlServiceProvider::class, 38 | willvincent\Feeds\FeedsServiceProvider::class, 39 | Intervention\Image\ImageServiceProvider::class, 40 | jeremykenedy\laravelpodcast\PodcastServiceProvider::class, 41 | ``` 42 | 43 | 3. Register the dependencies aliases with laravel in `config/app.php` section under `aliases` with the following: 44 | 45 | ``` 46 | 'Form' => Collective\Html\FormFacade::class, 47 | 'Html' => Collective\Html\HtmlFacade::class, 48 | 'Feeds' => willvincent\Feeds\Facades\FeedsFacade::class, 49 | 'Image' => Intervention\Image\Facades\Image::class, 50 | ``` 51 | 52 | 4. Publish the packages assets by running the following from your projects root folder: 53 | 54 | ``` 55 | php artisan vendor:publish 56 | ``` 57 | 58 | 5. Configure your projects `.env` file and add the following: 59 | 60 | ``` 61 | DB_CHARSET=utf8mb4 62 | DB_COLLATION=utf8mb4_unicode_ci 63 | ``` 64 | 65 | 6. Update the datebase by running the following from your projects root folder: 66 | 67 | ``` 68 | php artisan migrate 69 | ``` 70 | 71 | ## Routes 72 | 73 | * ```/podcast``` 74 | * ```/podcasts``` 75 | * ```/podcast/search``` 76 | * ```/podcasts/manage``` 77 | * ```/podcasts/player``` 78 | * ```/podcasts/settings``` 79 | * ```/podcasts/favorites``` 80 | * ```/podcasts/auto-update``` 81 | 82 | ## Required Packages 83 | (included in this package) 84 | 85 | * [laravelcollective/html](https://packagist.org/packages/laravelcollective/html) 86 | * [willvincent/feeds](https://packagist.org/packages/willvincent/feeds) 87 | * [intervention/image](https://packagist.org/packages/intervention/image) 88 | 89 | ## Screenshots 90 | 91 | ![Home/Listen Page](https://s3-us-west-2.amazonaws.com/github-project-images/laravel-podcast/1-home.jpg) 92 | ![Manage Page](https://s3-us-west-2.amazonaws.com/github-project-images/laravel-podcast/2-manage.jpg) 93 | ![Favorites Page](https://s3-us-west-2.amazonaws.com/github-project-images/laravel-podcast/3-favorites.jpg) 94 | ![Search Results Page](https://s3-us-west-2.amazonaws.com/github-project-images/laravel-podcast/4-search.jpg) 95 | ![Mark as Read Modal](https://s3-us-west-2.amazonaws.com/github-project-images/laravel-podcast/7-modal-read.jpg) 96 | ![Mark All as Read Modal](https://s3-us-west-2.amazonaws.com/github-project-images/laravel-podcast/8-modal-all-read.jpg) 97 | ![Add RSS Feed Modal](https://s3-us-west-2.amazonaws.com/github-project-images/laravel-podcast/9-modal-add.jpg) 98 | ![Delete RSS Feed Modal](https://s3-us-west-2.amazonaws.com/github-project-images/laravel-podcast/10-modal-delete.jpg) 99 | 100 | ## License 101 | 102 | laravelpodcast - A Laravel package is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT) 103 | -------------------------------------------------------------------------------- /src/.env.example: -------------------------------------------------------------------------------- 1 | APP_ENV=local 2 | APP_KEY= 3 | APP_DEBUG=true 4 | APP_LOG_LEVEL=debug 5 | APP_URL=http://localhost 6 | 7 | DB_CONNECTION=mysql 8 | DB_HOST=127.0.0.1 9 | DB_PORT=3306 10 | DB_DATABASE=laravelPodcast 11 | DB_USERNAME=homestead 12 | DB_PASSWORD=secret 13 | DB_CHARSET=utf8mb4 14 | DB_COLLATION=utf8mb4_unicode_ci 15 | 16 | BROADCAST_DRIVER=log 17 | CACHE_DRIVER=file 18 | SESSION_DRIVER=file 19 | QUEUE_DRIVER=sync 20 | 21 | REDIS_HOST=127.0.0.1 22 | REDIS_PASSWORD=null 23 | REDIS_PORT=6379 24 | 25 | MAIL_DRIVER=smtp 26 | MAIL_HOST=mailtrap.io 27 | MAIL_PORT=2525 28 | MAIL_USERNAME=null 29 | MAIL_PASSWORD=null 30 | MAIL_ENCRYPTION=null 31 | 32 | PUSHER_APP_ID= 33 | PUSHER_KEY= 34 | PUSHER_SECRET= 35 | -------------------------------------------------------------------------------- /src/.env.travis: -------------------------------------------------------------------------------- 1 | APP_ENV=local 2 | APP_KEY= 3 | APP_DEBUG=true 4 | APP_LOG_LEVEL=debug 5 | APP_URL=http://localhost 6 | 7 | DB_CONNECTION=mysql 8 | DB_HOST=127.0.0.1 9 | DB_PORT=3306 10 | DB_DATABASE=laravelPodcast 11 | DB_USERNAME=root 12 | DB_PASSWORD= 13 | DB_CHARSET=utf8 14 | DB_COLLATION=utf8_unicode_ci 15 | 16 | BROADCAST_DRIVER=log 17 | CACHE_DRIVER=file 18 | SESSION_DRIVER=file 19 | QUEUE_DRIVER=sync 20 | 21 | REDIS_HOST=127.0.0.1 22 | REDIS_PASSWORD=null 23 | REDIS_PORT=6379 24 | 25 | MAIL_DRIVER=smtp 26 | MAIL_HOST=mailtrap.io 27 | MAIL_PORT=2525 28 | MAIL_USERNAME=null 29 | MAIL_PASSWORD=null 30 | MAIL_ENCRYPTION=null 31 | 32 | PUSHER_APP_ID= 33 | PUSHER_KEY= 34 | PUSHER_SECRET= 35 | -------------------------------------------------------------------------------- /src/PodcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadRoutesFrom(__DIR__.'/routes/web.php'); 17 | $this->loadMigrationsFrom(__DIR__.'/database/migrations'); 18 | $this->loadViewsFrom(__DIR__.'/resources/views/', 'laravelpodcast'); 19 | 20 | $this->publishes([ 21 | __DIR__.'/public/css' => public_path('vendor/laravelpodcast'), 22 | ], 'podcast'); 23 | } 24 | 25 | /** 26 | * Register the application services. 27 | * 28 | * @return void 29 | */ 30 | public function register() 31 | { 32 | include __DIR__.'/routes/web.php'; 33 | $this->app->make('jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastsController'); 34 | $this->app->make('jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastItemsController'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/app/Console/Commands/UpdatePodcastItems.php: -------------------------------------------------------------------------------- 1 | select('id', 'feed_url', 'machine_name') 46 | ->groupBy('id')->get(); 47 | 48 | foreach ($uniquePodcasts as $podcast) { 49 | $usersSubscribedToThisPodcast = DB::table('podcasts') 50 | ->select('user_id', 'id as podcast_id') 51 | ->where('machine_name', '=', $podcast->machine_name) 52 | ->get(); 53 | 54 | $items = Feeds::make($podcast->feed_url)->get_items(); 55 | 56 | // Calculate 48 hours ago 57 | $yesterday = time() - (24 * 2 * 60 * 60); 58 | 59 | foreach ($items as $item) { 60 | $itemPubDate = $item->get_date(); 61 | 62 | if ($item->get_date('U') > $yesterday) { 63 | 64 | // new items 65 | foreach ($usersSubscribedToThisPodcast as $subscriber) { 66 | $podcastItemsCount = DB::table('podcast_items') 67 | ->select('user_id', 'title', 'podcast_id') 68 | ->where('title', '=', strip_tags($item->get_title())) 69 | ->where('user_id', '=', $subscriber->user_id) 70 | ->where('podcast_id', '=', $subscriber->podcast_id) 71 | ->count(); 72 | 73 | // if this item is not already in the DB 74 | if ($podcastItemsCount == 0) { 75 | PodcastItem::create([ 76 | 'user_id' => $subscriber->user_id, 77 | 'title' => strip_tags($item->get_title()), 78 | 'description' => strip_tags(str_limit($item->get_description(), 100)), 79 | 'published_at' => $item->get_date('Y-m-d'), 80 | 'url' => $item->get_permalink(), 81 | 'audio_url' => $item->get_enclosure()->get_link(), 82 | 'podcast_id' => $subscriber->podcast_id, 83 | ]); 84 | } 85 | } 86 | } else { 87 | break; 88 | } 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire') 29 | // ->hourly(); 30 | 31 | $schedule->command('updatePodcastItems') 32 | ->twiceDaily(); 33 | } 34 | 35 | /** 36 | * Register the Closure based commands for the application. 37 | * 38 | * @return void 39 | */ 40 | protected function commands() 41 | { 42 | require base_path('routes/console.php'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/app/Http/Controllers/PodcastItemsController.php: -------------------------------------------------------------------------------- 1 | middleware('auth'); 21 | } 22 | 23 | /** 24 | * [markAsRead mark a podcast item as read]. 25 | * 26 | * @return array 27 | */ 28 | public function markAsRead(Request $request) 29 | { 30 | $result['status'] = 0; 31 | $result['message'] = 'Something went wrong, please try again'; 32 | 33 | $podcastItemId = trim(strip_tags($request->itemId)); 34 | 35 | $item = DB::table('podcast_items')->select('user_id') 36 | ->where('user_id', '=', Auth::user()->id) 37 | ->where('id', '=', $podcastItemId) 38 | ->first(); 39 | 40 | // if item with id exists in DB and is owned by the authenticated user 41 | if ($item) { 42 | $podcastItem = PodcastItem::findOrFail($podcastItemId); 43 | $podcastItem->is_mark_as_read = 1; 44 | $podcastItem->save(); 45 | $result['status'] = 1; 46 | $result['message'] = 'Marked as read'; 47 | } 48 | 49 | return $result; 50 | } 51 | 52 | /** 53 | * [markAllPrevAsRead mark all previous item in a podcast as read]. 54 | * 55 | * @return array 56 | */ 57 | public function markAllPrevAsRead(Request $request) 58 | { 59 | $result['status'] = 0; 60 | $result['message'] = 'Something went wrong, please try again'; 61 | 62 | $podcastItemId = trim(strip_tags($request->itemId)); 63 | 64 | $item = DB::table('podcast_items')->select('published_at', 'podcast_id') 65 | ->where('user_id', '=', Auth::user()->id) 66 | ->where('id', '=', $podcastItemId) 67 | ->first(); 68 | if ($item) { 69 | $items = DB::table('podcast_items') 70 | ->select('id', 'published_at') 71 | ->where('user_id', '=', Auth::user()->id) 72 | ->where('podcast_id', '=', $item->podcast_id) 73 | ->where('is_mark_as_read', '!=', 1) 74 | ->where('published_at', '<', $item->published_at) 75 | ->get(); 76 | 77 | $podcastItemIds = []; 78 | 79 | foreach ($items as $record) { 80 | $record = PodcastItem::findOrFail($record->id); 81 | $record->is_mark_as_read = 1; 82 | $record->save(); 83 | 84 | array_push($podcastItemIds, $record->id); 85 | } 86 | 87 | // @todo need to limit the item ids sent to 15 (descending order) 88 | 89 | $result['status'] = 1; 90 | $result['data'] = $podcastItemIds; 91 | $result['message'] = 'Previous items in this podcast has been marked as read'; 92 | } 93 | 94 | return $result; 95 | } 96 | 97 | /** 98 | * [markAsFavorite mark a podcast item as favorite]. 99 | * 100 | * @return array 101 | */ 102 | public function markAsFavorite(Request $request) 103 | { 104 | $result['status'] = 0; 105 | $result['message'] = 'Something went wrong, please try again'; 106 | 107 | $podcastitemId = trim(strip_tags($request->itemId)); 108 | 109 | $item = DB::table('podcast_items')->select('user_id') 110 | ->where('user_id', '=', Auth::user()->id) 111 | ->where('id', '=', $podcastitemId) 112 | ->first(); 113 | 114 | // if item with id exists in DB and is owned by the authenticated user 115 | if ($item) { 116 | $podcastItem = PodcastItem::findOrFail($podcastitemId); 117 | $result['currentValue'] = !$podcastItem->is_mark_as_favorite; 118 | $podcastItem->is_mark_as_favorite = !$podcastItem->is_mark_as_favorite; // opposite of current value 119 | $podcastItem->save(); 120 | 121 | $result['status'] = 1; 122 | $result['message'] = 'Favorite Updated'; 123 | } 124 | 125 | return $result; 126 | } 127 | 128 | /** 129 | * Return a view to display item search results for a given query. 130 | */ 131 | public function search(Request $request) 132 | { 133 | $query = trim(strip_tags($request->query('query'))); 134 | $items = DB::table('podcast_items') 135 | ->where('title', 'LIKE', "%$query%") 136 | ->orWhere('description', 'LIKE', "%$query%") 137 | ->get(); 138 | 139 | return view('laravelpodcast::podcasts.searchresults')->with([ 140 | 'items' => $items, 141 | 'query' => $query, 142 | ]); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/app/Http/Controllers/PodcastsController.php: -------------------------------------------------------------------------------- 1 | middleware('auth'); 25 | } 26 | 27 | /** 28 | * Show the application dashboard. 29 | * 30 | * @return \Illuminate\Http\Response 31 | */ 32 | public function show($id) 33 | { 34 | $user = Auth::user(); 35 | 36 | $podcast_items = DB::table('podcast_items') 37 | ->where('user_id', '=', $user->id) 38 | ->where('is_mark_as_read', '!=', 1) 39 | ->orderBy('published_at', 'desc')->paginate(15); 40 | 41 | $podcasts = DB::table('podcasts') 42 | ->where('user_id', '=', $user->id) 43 | ->get(); 44 | 45 | $data = [ 46 | 'podcasts' => $podcasts, 47 | 'podcast_items' => $podcast_items, 48 | 'user' => $user, 49 | ]; 50 | 51 | return view('laravelpodcast::podcasts.list', $data); 52 | } 53 | 54 | public function index() 55 | { 56 | $user = Auth::user(); 57 | 58 | $podcast_items = DB::table('podcast_items') 59 | ->where('user_id', '=', $user->id) 60 | ->where('is_mark_as_read', '!=', 1) 61 | ->orderBy('published_at', 'desc')->paginate(15); 62 | 63 | $podcasts = DB::table('podcasts') 64 | ->where('user_id', '=', $user->id) 65 | ->get(); 66 | 67 | $data = [ 68 | 'podcasts' => $podcasts, 69 | 'podcast_items' => $podcast_items, 70 | 'user' => $user, 71 | ]; 72 | 73 | return view('laravelpodcast::podcasts.list', $data); 74 | } 75 | 76 | /** 77 | * Return a view to manage podcasts. 78 | * 79 | * @return view 80 | */ 81 | public function manage() 82 | { 83 | $user = Auth::user(); 84 | 85 | $podcasts = DB::table('podcasts') 86 | ->where('user_id', '=', $user->id) 87 | ->get(); 88 | 89 | $data = [ 90 | 'podcasts' => $podcasts, 91 | 'user' => $user, 92 | ]; 93 | 94 | return view('laravelpodcast::podcasts.manage', $data); 95 | } 96 | 97 | /** 98 | * Return the list of favorites for a user to a view. 99 | * 100 | * @return [type] [description] 101 | */ 102 | public function favorites() 103 | { 104 | $podcastItems = DB::table('podcast_items') 105 | ->where('user_id', '=', Auth::user()->id) 106 | ->where('is_mark_as_favorite', '!=', 0) 107 | ->orderBy('published_at', 'desc')->paginate(15); 108 | 109 | $data = [ 110 | 'podcastItems' => $podcastItems, 111 | ]; 112 | 113 | return view('laravelpodcast::podcasts.favorites', $data); 114 | } 115 | 116 | /** 117 | * Return a view to manage settings. 118 | * 119 | * @return view 120 | */ 121 | public function settings() 122 | { 123 | return view('laravelpodcast::podcasts.settings'); 124 | } 125 | 126 | /** 127 | * Store a newly created podcast in storage. 128 | * 129 | * @param \Illuminate\Http\Request $request 130 | * 131 | * @return \Illuminate\Http\Response 132 | */ 133 | public function store(Request $request) 134 | { 135 | $this->validate($request, [ 136 | 'feed_url' => 'required|unique:podcasts', 137 | ]); 138 | 139 | // create "images" directory under "public" directory if it doesn't exist 140 | if (!File::exists(public_path().'/images')) { 141 | File::makeDirectory(public_path().'/images'); 142 | } 143 | 144 | $user = Auth::user(); 145 | 146 | if ($request->feed_url) { 147 | $feed = Feeds::make($request->feed_url); 148 | $feed->force_feed(true); 149 | $feed->handle_content_type(); 150 | $podcastName = $feed->get_title(); 151 | 152 | if ($podcastName && $podcastName != '') { 153 | // check if the feed's first item has an audio file in its enclosure 154 | if (strpos($feed->get_items()[0]->get_enclosure()->get_type(), 'audio') !== false) { 155 | $podcastMachineName = strtolower(preg_replace('/\s+/', '', $podcastName)); 156 | 157 | // Save the feed thumbnail to file system and save file path to database 158 | $img = Image::make($feed->get_image_url())->resize(100, 100); 159 | $img->save(public_path('images/'.$podcastMachineName.'.png')); 160 | 161 | Podcast::create([ 162 | 'name' => $podcastName ? $podcastName : '', 163 | 'machine_name' => $podcastMachineName, 164 | 'feed_url' => $request->feed_url, 165 | 'feed_thumbnail_location' => 'images/'.$podcastMachineName.'.png', 166 | 'user_id' => $user->id, 167 | 'web_url' => $feed->get_link(), 168 | ]); 169 | 170 | foreach ($feed->get_items() as $item) { 171 | PodcastItem::create([ 172 | 'podcast_id' => DB::table('podcasts') 173 | ->select('id', 'machine_name') 174 | ->where('machine_name', '=', $podcastMachineName)->first()->id, 175 | 'user_id' => $user->id, 176 | 'url' => $item->get_permalink(), 177 | 'audio_url' => $item->get_enclosure()->get_link(), 178 | 'title' => $item->get_title(), 179 | 'description' => trim(strip_tags(str_limit($item->get_description(), 200))), 180 | 'published_at' => $item->get_date('Y-m-d H:i:s'), 181 | ]); 182 | } 183 | 184 | return back()->with('success', 'Successfully added the Podcast!'); 185 | } else { 186 | return back()->with('message', 'This doesn\'t seem to be an RSS feed with audio files. Please try another feed.'); 187 | } 188 | } else { 189 | return back()->with('error', 'Sorry, this feed cannot be imported. Please try another feed.'); 190 | } 191 | } else { 192 | return back()->with('error', 'Invalid feed URL given.'); 193 | } 194 | } 195 | 196 | /* 197 | * Show the form for editing the specified resource. 198 | * 199 | * @param int $id 200 | * @return \Illuminate\Http\Response 201 | */ 202 | public function edit($id) 203 | { 204 | // 205 | } 206 | 207 | /** 208 | * Update the specified resource in storage. 209 | * 210 | * @param \Illuminate\Http\Request $request 211 | * @param int $id 212 | * 213 | * @return \Illuminate\Http\Response 214 | */ 215 | public function update(Request $request, $id) 216 | { 217 | // 218 | } 219 | 220 | /** 221 | * Delete a podcast. 222 | * 223 | * @param int $id 224 | * 225 | * @return \Illuminate\Http\Response 226 | */ 227 | public function destroy($id) 228 | { 229 | Podcast::findOrFail($id)->delete(); 230 | 231 | return back()->with('success', 'Successfully deleted the Podcast!'); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/app/Models/Podcast.php: -------------------------------------------------------------------------------- 1 | hasMany('App\Models\PodcastItem'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/Models/PodcastItem.php: -------------------------------------------------------------------------------- 1 | belongsTo('App\Models\Podcast'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/Models/User.php: -------------------------------------------------------------------------------- 1 | hasMany('App\Models\Podcast'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/database/migrations/2017_01_08_055955_create_podcasts_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name'); 19 | $table->string('machine_name'); 20 | $table->string('web_url'); 21 | $table->string('feed_url'); 22 | $table->string('feed_thumbnail_location')->nullable(); 23 | $table->integer('user_id')->unsigned(); 24 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 25 | $table->unique(['name', 'machine_name', 'user_id']); 26 | $table->timestamps(); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::dropIfExists('podcasts'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/database/migrations/2017_01_08_060244_create_podcast_items_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('user_id')->unsigned(); 19 | $table->longText('title'); 20 | $table->string('description', 500); 21 | $table->string('url'); 22 | $table->string('audio_url'); 23 | $table->date('published_at'); 24 | $table->boolean('is_mark_as_read')->default(0); 25 | $table->boolean('is_mark_as_favorite')->default(0); 26 | $table->integer('podcast_id')->unsigned(); 27 | $table->foreign('podcast_id')->references('id')->on('podcasts')->onDelete('cascade'); 28 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 29 | $table->timestamps(); 30 | }); 31 | } 32 | 33 | /** 34 | * Reverse the migrations. 35 | * 36 | * @return void 37 | */ 38 | public function down() 39 | { 40 | Schema::drop('podcast_items'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/public/css/icons/ic_add_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_add_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_add_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_add_white_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_clear_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_clear_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_clear_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_clear_white_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_delete_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_delete_black_24dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_delete_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_delete_white_24dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_done_all_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_done_all_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_done_all_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_done_all_white_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_done_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_done_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_done_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_done_white_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_event_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_event_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_event_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_event_white_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_favorite_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_favorite_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_favorite_border_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_favorite_border_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_favorite_grey600_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_favorite_grey600_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_favorite_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_favorite_white_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_file_download_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_file_download_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_file_download_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_file_download_white_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_play_arrow_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_play_arrow_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_play_arrow_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_play_arrow_white_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_play_circle_filled_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_play_circle_filled_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_play_circle_filled_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_play_circle_filled_white_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_play_circle_outline_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_play_circle_outline_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_play_circle_outline_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_play_circle_outline_white_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_search_black_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_search_black_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/ic_search_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/ic_search_white_36dp.png -------------------------------------------------------------------------------- /src/public/css/icons/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremykenedy/laravelpodcast/972d83822ab05e4e4a59e4738cd61b5ae65e3ddc/src/public/css/icons/play.png -------------------------------------------------------------------------------- /src/resources/assets/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | // Colors 2 | 3 | //$baseThemeColor: #2296f3; 4 | $baseThemeColor: #000000; 5 | 6 | $themeColor0: rgba($baseThemeColor, 0%); 7 | $themeColor1: lighten($baseThemeColor, 100%); 8 | $themeColor2: $baseThemeColor; 9 | $themeColor3: lighten($baseThemeColor, 13%); 10 | $themeColor4: lighten($baseThemeColor, 90%); 11 | $themeColor5: lighten($baseThemeColor, 25%); 12 | $themeColor6: lighten($baseThemeColor, 17%); 13 | $themeColor7: lighten($baseThemeColor, 40%); 14 | $themeColor8: lighten($baseThemeColor, 60%); 15 | $themeColor9: lighten($baseThemeColor, 6%); 16 | $themeColor10: lighten($baseThemeColor, 4%); 17 | 18 | // Body 19 | $body-bg: $themeColor3; 20 | $body-color: $themeColor4; 21 | 22 | // Borders 23 | $laravel-border-color: darken($body-bg, 10%); 24 | $list-group-border: $laravel-border-color; 25 | $navbar-default-border: $laravel-border-color; 26 | $panel-default-border: $laravel-border-color; 27 | $panel-inner-border: $laravel-border-color; 28 | $standardRadius: 4px; 29 | 30 | // Brands 31 | $brand-primary: #3097D1; 32 | $brand-info: #8eb4cb; 33 | $brand-success: #2ab27b; 34 | $brand-warning: #cbb956; 35 | $brand-danger: #bf5329; 36 | 37 | // Typography 38 | $font-family-sans-serif: "Raleway", sans-serif; 39 | $font-size-base: 14px; 40 | $line-height-base: 1.6; 41 | $text-color: #636b6f; 42 | 43 | // Breakpoints 44 | $tablet: 768px; 45 | $desktop: 992px; 46 | 47 | // Transitions 48 | $transition-speed: 0.15s; 49 | 50 | // Navbar 51 | $navbar-default-bg: $themeColor1; 52 | 53 | // Buttons 54 | $btn-default-color: $text-color; 55 | 56 | // Inputs 57 | $input-border: lighten($text-color, 40%); 58 | $input-border-focus: lighten($brand-primary, 25%); 59 | $input-color-placeholder: lighten($text-color, 30%); 60 | 61 | // Panels 62 | $panel-default-heading-bg: $themeColor1; -------------------------------------------------------------------------------- /src/resources/assets/sass/app.scss: -------------------------------------------------------------------------------- 1 | 2 | // Fonts 3 | //@import url(https://fonts.googleapis.com/css?family=Raleway:300,400,600); 4 | 5 | // Variables & Mixins 6 | @import "variables"; 7 | @import "partials/mixins"; 8 | 9 | // Bootstrap 10 | //@import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap"; 11 | 12 | // Our App 13 | @import "partials/base"; 14 | @import "partials/buttons"; 15 | @import "partials/modals"; 16 | @import "partials/panels"; 17 | @import "partials/pagination"; 18 | @import "partials/podcast"; 19 | @import "partials/player"; -------------------------------------------------------------------------------- /src/resources/assets/sass/partials/_base.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background: $body-bg; 3 | color: $body-color; 4 | 5 | .navbar-inverse { 6 | background-color: $themeColor9; 7 | border-color: $themeColor10; 8 | } 9 | .navbar-inverse .navbar-collapse, 10 | .navbar-inverse .navbar-form { 11 | border-color: $themeColor10; 12 | } 13 | .navbar-inverse .navbar-brand, 14 | .navbar-inverse .navbar-nav > li > a { 15 | border-color: $themeColor7; 16 | color: $themeColor7; 17 | } 18 | .navbar-inverse .navbar-toggle { 19 | border-color: $themeColor8; 20 | } 21 | .navbar-inverse .navbar-toggle:hover, 22 | .navbar-inverse .navbar-toggle:active, 23 | .navbar-inverse .navbar-toggle:focus { 24 | background-color: $themeColor6; 25 | } 26 | .navbar-inverse .navbar-nav > li > a:hover { 27 | color: themeColor1; 28 | background-color: $themeColor2; 29 | } 30 | .navbar-inverse .navbar-nav > .active > a, 31 | .navbar-inverse .navbar-nav > .active > a:hover, 32 | .navbar-inverse .navbar-nav > .active > a:focus { 33 | color: themeColor1; 34 | background-color: $themeColor10; 35 | } 36 | 37 | &.bg1 { 38 | background: $themeColor1; 39 | } 40 | &.bg2 { 41 | background: $themeColor2; 42 | } 43 | &.bg3 { 44 | background: $themeColor3; 45 | } 46 | &.bg4 { 47 | background: $themeColor4; 48 | } 49 | &.bg5 { 50 | background: $themeColor5; 51 | } 52 | &.bg6 { 53 | background: $themeColor6; 54 | } 55 | &.bg7 { 56 | background: $themeColor7; 57 | } 58 | &.bg8 { 59 | background: $themeColor8; 60 | } 61 | &.bg9 { 62 | background: $themeColor9; 63 | } 64 | &.bg10 { 65 | background: $themeColor10; 66 | } 67 | 68 | } 69 | 70 | .page-title + hr { 71 | opacity: .5; 72 | } 73 | 74 | 75 | @media(max-width: $tablet - 1px) { 76 | .navbar-form { 77 | margin: 7px 0; 78 | } 79 | } -------------------------------------------------------------------------------- /src/resources/assets/sass/partials/_buttons.scss: -------------------------------------------------------------------------------- 1 | a, 2 | .play, 3 | .mark-as-favorite, 4 | .mark-as-read, 5 | .mark-all-prev-read, 6 | .download, 7 | .podcast-action-list, 8 | .btn { 9 | @include transitions(); 10 | cursor: pointer; 11 | } 12 | .btn-delete, 13 | .btn-delete:hover, 14 | .btn-delete:focus, 15 | .btn-delete:active { 16 | border: none; 17 | outline: 0 !important; 18 | box-shadow: none; 19 | background: $themeColor0; 20 | } 21 | .btn-add-podcast { 22 | a { 23 | color: inherit; 24 | margin: -.5em 0 0 0; 25 | @include transitionless(); 26 | } 27 | } 28 | 29 | @media(min-width: $desktop) { 30 | .btn-add-podcast { 31 | a { 32 | margin: 1em 0 0 0; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/resources/assets/sass/partials/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Vendor Prefixing Mixin 2 | @mixin vendor($property, $value) { 3 | -webkit-#{$property}: $value; 4 | -moz-#{$property}: $value; 5 | -ms-#{$property}: $value; 6 | -o-#{$property}: $value; 7 | #{$property}: $value; 8 | } 9 | 10 | @mixin transitions() { 11 | @include vendor(transition, $transition-speed ease-in-out); 12 | } 13 | 14 | @mixin transitionless() { 15 | @include vendor(transition, none); 16 | } 17 | 18 | @mixin draggless() { 19 | @include vendor(user-drag, none); 20 | @include vendor(user-select, none); 21 | } -------------------------------------------------------------------------------- /src/resources/assets/sass/partials/_modals.scss: -------------------------------------------------------------------------------- 1 | .modal { 2 | z-index: 9999; 3 | 4 | .modal-header { 5 | @include vendor(border-radius, $standardRadius $standardRadius 0 0); 6 | } 7 | .modal-body { 8 | color: $themeColor3; 9 | } 10 | &.modal-primary .modal-header { 11 | color: $themeColor1; 12 | border-bottom: 1px solid $brand-primary; 13 | background-color: $brand-primary; 14 | } 15 | &.modal-warning .modal-header { 16 | color: $themeColor1; 17 | border-bottom: 1px solid $brand-warning; 18 | background-color: $brand-warning; 19 | } 20 | &.modal-info .modal-header { 21 | color: $themeColor1; 22 | border-bottom: 1px solid $brand-info; 23 | background-color: $brand-info; 24 | } 25 | &.modal-success .modal-header { 26 | color: $themeColor1; 27 | border-bottom: 1px solid $brand-success; 28 | background-color: $brand-success; 29 | } 30 | &.modal-danger .modal-header { 31 | color: $themeColor1; 32 | border-bottom: 1px solid $brand-danger; 33 | background-color: $brand-danger; 34 | } 35 | } -------------------------------------------------------------------------------- /src/resources/assets/sass/partials/_pagination.scss: -------------------------------------------------------------------------------- 1 | .pagination>.disabled>a, 2 | .pagination>.disabled>a:focus, 3 | .pagination>.disabled>a:hover, 4 | .pagination>.disabled>span, 5 | .pagination>.disabled>span:focus, 6 | .pagination>.disabled>span:hover { 7 | background: $themeColor0; 8 | border-color: $themeColor0; 9 | opacity: .2; 10 | } 11 | .pagination>li>a, 12 | .pagination>li>span { 13 | color: $themeColor1; 14 | border: 1px solid $themeColor0; 15 | background: $themeColor0; 16 | padding: 3px 12px 4px; 17 | font-size: 1.3em; 18 | } 19 | .pagination>li:first-child>a, 20 | .pagination>li:first-child>span, 21 | .pagination>li:last-child>a, 22 | .pagination>li:last-child>span { 23 | @include vendor(border-radius, 0); 24 | } 25 | .pagination>.active>a, 26 | .pagination>.active>a:focus, 27 | .pagination>.active>a:hover, 28 | .pagination>.active>span, 29 | .pagination>.active>span:focus, 30 | .pagination>.active>span:hover { 31 | z-index: 3; 32 | color: $themeColor1; 33 | background-color: rgba($themeColor2, .8); 34 | border-color: rgba($themeColor2, .8); 35 | cursor: default; 36 | } -------------------------------------------------------------------------------- /src/resources/assets/sass/partials/_panels.scss: -------------------------------------------------------------------------------- 1 | .panel { 2 | background: $themeColor6; 3 | border-color: $themeColor6; 4 | color: $themeColor4; 5 | border: none; 6 | 7 | &.panel-default > .panel-heading { 8 | background: $themeColor5; 9 | border-color: $themeColor5; 10 | color: $themeColor4; 11 | border: none; 12 | } 13 | } -------------------------------------------------------------------------------- /src/resources/assets/sass/partials/_player.scss: -------------------------------------------------------------------------------- 1 | #player-container { 2 | display: none; 3 | 4 | .close { 5 | position: absolute; 6 | top: 0; 7 | right: 10px; 8 | font-size: 3em; 9 | font-weight: 100; 10 | color: $themeColor1; 11 | text-shadow: 0 1px 0 $themeColor2; 12 | opacity: .7; 13 | 14 | &:hover { 15 | opacity: .8; 16 | } 17 | 18 | &:active { 19 | opacity: 1; 20 | } 21 | } 22 | } 23 | 24 | #player { 25 | width: 100%; 26 | padding: 0; 27 | margin:0; 28 | } 29 | 30 | .alert-playing { 31 | border: none; 32 | color: $themeColor4; 33 | background: rgba($themeColor2, .85); 34 | position: fixed; 35 | z-index: 999; 36 | bottom: 0; 37 | margin: 0; 38 | width: 100%; 39 | 40 | .now-playing { 41 | text-align: center; 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/resources/assets/sass/partials/_podcast.scss: -------------------------------------------------------------------------------- 1 | .podcast-container { 2 | background-color: $themeColor3; 3 | padding: 1em; 4 | margin-bottom: 1em; 5 | text-align: center; 6 | } 7 | 8 | .podcast-item-row { 9 | 10 | background: rgba($themeColor2, .3); 11 | margin-bottom: 1.25em; 12 | 13 | .fa { 14 | color: $themeColor1; 15 | @include transitions(); 16 | 17 | &:hover { 18 | color: $themeColor8; 19 | } 20 | } 21 | 22 | .fa-stack { 23 | margin: -3px 0 0; 24 | 25 | &:hover { 26 | .fa { 27 | color: $themeColor8; 28 | @include transitions(); 29 | } 30 | } 31 | } 32 | 33 | .fa-stack:hover .fa.fa-inverse, 34 | .fa.fa-inverse:hover, 35 | .fa.fa-inverse { 36 | color: $themeColor3; 37 | } 38 | 39 | &.active { 40 | background: $themeColor5; 41 | margin-bottom: 1.25em; 42 | 43 | .player-action-list { 44 | .play { 45 | pointer-events: none; 46 | cursor: default; 47 | color: $themeColor7; 48 | .fa { 49 | color: $themeColor7; 50 | } 51 | } 52 | } 53 | } 54 | } 55 | 56 | .podcast-thumbnail-container { 57 | text-align: center; 58 | } 59 | .podcast-thumbnail { 60 | margin: 2em auto .5em; 61 | display: block; 62 | @include draggless(); 63 | 64 | } 65 | .podcast-item-title { 66 | margin: 0 auto .5em; 67 | font-size: 1.3em; 68 | text-align: center; 69 | font-weight: 400; 70 | 71 | a { 72 | text-decoration: none; 73 | color: $themeColor4; 74 | } 75 | } 76 | .podcast-title { 77 | text-align: center; 78 | margin: 0 auto 5px; 79 | 80 | small { 81 | font-weight: 300; 82 | } 83 | } 84 | .container-podcast-manage { 85 | .podcast-title { 86 | text-align: center; 87 | } 88 | } 89 | .player-action-list { 90 | text-align: center; 91 | margin: 0 0 2em; 92 | 93 | .fa { 94 | font-size: 1.75em; 95 | } 96 | 97 | } 98 | 99 | //.favorites { 100 | // .player-action-list { 101 | // li { 102 | // &:nth-child(3), 103 | // &:nth-child(4) { 104 | // display: none; 105 | // } 106 | // } 107 | // } 108 | // } 109 | 110 | 111 | @media(min-width: $tablet) { 112 | .podcast-container { 113 | margin-bottom: 2em; 114 | } 115 | .podcast-title { 116 | text-align: left; 117 | margin: .9em 0 3px; 118 | } 119 | 120 | .podcast-item-title { 121 | margin: 0 0 .5em; 122 | font-size: 1.3em; 123 | text-align: left; 124 | font-weight: 400; 125 | } 126 | .podcast-item-description { 127 | line-height: 1.3; 128 | margin: 0; 129 | opacity: .8; 130 | } 131 | 132 | .podcast-thumbnail { 133 | margin: 1.5em auto .5em; 134 | } 135 | .player-action-list { 136 | text-align: left; 137 | margin: 0; 138 | 139 | .fa { 140 | font-size: 1em; 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/resources/views/layouts/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{-- CSRF Token --}} 9 | 10 | 11 | @if (trim($__env->yieldContent('template_title')))@yield('template_title') | @endif {{ config('app.name', 'Laravel Podcast') }} 12 | 13 | {{-- Styles --}} 14 | 15 | 16 | 17 | @yield('header-style') 18 | 19 | {{-- Scripts --}} 20 | 25 | 26 | 27 |
28 | 29 | @include('laravelpodcast::partials.nav') 30 | @yield('content') 31 | 32 |
33 | 34 | {{-- Scripts --}} 35 | 36 | @yield('footer-scripts') 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/resources/views/modals/modal-add.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/resources/views/modals/modal-delete.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/resources/views/modals/modal-markAllRead.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/resources/views/modals/modal-markRead.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/resources/views/partials/form-status.blade.php: -------------------------------------------------------------------------------- 1 | @if (session('message')) 2 |
3 | × 4 |

Message

5 | {{ session('message') }} 6 |
7 | @endif 8 | 9 | @if (session('success')) 10 |
11 | × 12 |

Success

13 | {{ session('success') }} 14 |
15 | @endif 16 | 17 | @if (session('error')) 18 |
19 | × 20 |

21 | 22 | Error 23 |

24 | {{ session('error') }} 25 |
26 | @endif 27 | 28 | @if (count($errors) > 0) 29 |
30 | × 31 |

32 | 33 | {{ Lang::get('auth.whoops') }} {{ Lang::get('auth.someProblems') }} 34 |

35 | 40 |
41 | @endif -------------------------------------------------------------------------------- /src/resources/views/partials/nav.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/resources/views/podcasts/favorites.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laravelpodcast::layouts.app') 2 | 3 | @section('template_title') 4 | Favorites 5 | @endsection 6 | 7 | @section('template_body_classes') 8 | bg6 9 | @endsection 10 | 11 | @section('content') 12 | @if($podcastItems) 13 | @include('laravelpodcast::podcasts.player') 14 | @endif 15 |
16 |
17 |
18 |

19 | Favorite Podcasts 20 |

21 |
22 |
23 |
24 |
25 |
26 | @if($podcastItems) 27 | 28 | @foreach ($podcastItems as $item) 29 | @include('laravelpodcast::podcasts.item') 30 | @endforeach 31 | 32 | {{ $podcastItems->render() }} 33 | 34 | @endif 35 | 36 | @if (count($podcastItems) === 0) 37 |

You have not favorited any podcasts

38 | @endif 39 |
40 |
41 |
42 | @endsection 43 | 44 | @section('footer-scripts') 45 | @include('laravelpodcast::scripts.podcast-scripts') 46 | @endsection -------------------------------------------------------------------------------- /src/resources/views/podcasts/item.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

5 | 6 | {{ date_format(date_create($item->published_at),'jS M Y') }} 7 | 8 |

9 |
10 |
11 |

12 | 13 | 14 | 15 |

16 |

17 | {{-- --}} 18 | {{ $item->title }} 19 | {{-- --}} 20 |

21 |

22 | {{ $item->description }} 23 |

24 | {{-- 25 | Read More 26 | --}} 27 |

28 |
29 |
    30 |
  • 31 | 32 | Play 33 |
  • 34 |
  • 35 | @if($item->is_mark_as_favorite) 36 | 37 | Marked as Favorite 38 | @else 39 | 40 | Not Marked as Favorite 41 | @endif 42 |
  • 43 | 44 |
  • 45 | 46 | 47 | Mark as read 48 | 49 |
  • 50 | 51 |
  • 52 | 53 | 54 | 55 | 56 | Mark all previous as read 57 | 58 |
  • 59 |
  • 60 | 61 | 62 | Download 63 | 64 |
  • 65 |
66 |
67 |
68 |
69 | 70 | -------------------------------------------------------------------------------- /src/resources/views/podcasts/list.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laravelpodcast::layouts.app') 2 | 3 | @section('template_title') 4 | Listen 5 | @endsection 6 | 7 | @section('content') 8 | 9 | @if($podcast_items) 10 | @include('laravelpodcast::podcasts.player') 11 | @endif 12 | 13 |
14 |
15 |
16 |

17 | My Podcast List 18 |

19 |
20 |
21 |
22 |
23 |
24 | @if($podcast_items) 25 | @foreach ($podcast_items as $item) 26 | @include('laravelpodcast::podcasts.item') 27 | @endforeach 28 |
29 | {{ $podcast_items->render() }} 30 |
31 | @endif 32 |
33 |
34 |
35 | 36 | @endsection 37 | 38 | @section('footer-scripts') 39 | 40 | @include('laravelpodcast::scripts.podcast-scripts') 41 | 42 | @endsection -------------------------------------------------------------------------------- /src/resources/views/podcasts/manage.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laravelpodcast::layouts.app') 2 | 3 | @section('template_title') 4 | Manage Podcasts 5 | @endsection 6 | 7 | @section('header-style') 8 | @endsection 9 | 10 | @section('template_body_classes') 11 | bg5 12 | @endsection 13 | 14 | @section('content') 15 |
16 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |

27 | Manage Podcast Feeds 28 |

29 |
30 | @include('laravelpodcast::partials.form-status') 31 |
32 |
33 |
34 | @if($podcasts->count() > 0) 35 | @foreach($podcasts as $cast) 36 |
37 |
38 | Added on {{ date('F d, Y', strtotime($cast->created_at)) }} 39 | 40 |

{{$cast->name}}

41 | 42 |

43 | {{-- --}} 44 | 45 | {{-- --}} 46 |

47 | 48 |
49 |
    50 |
  • 51 | {!! Form::open(array('url' => 'podcasts/' . $cast->id)) !!} 52 | {!! Form::hidden('_method', 'DELETE') !!} 53 | {!! Form::button(' Delete', array('class' => 'btn btn-delete','type' => 'button', 'data-toggle' => 'modal', 'data-target' => '#confirmDelete', 'data-title' => 'Delete Podcast', 'data-message' => 'Are you sure you want to delete this podcast ?')) !!} 54 | {!! Form::close() !!} 55 |
  • 56 |
57 |
58 |
59 |
60 | @endforeach 61 | @endif 62 |
63 |
64 | 65 | @include('laravelpodcast::modals.modal-add') 66 | @include('laravelpodcast::modals.modal-delete') 67 | 68 | @endsection 69 | 70 | @section('footer-scripts') 71 | @include('laravelpodcast::scripts.delete-modal-script') 72 | @include('laravelpodcast::scripts.add-modal-script') 73 | @endsection -------------------------------------------------------------------------------- /src/resources/views/podcasts/player.blade.php: -------------------------------------------------------------------------------- 1 |
2 | × 3 |
4 |
5 |
6 |
7 |

8 |
9 | 12 |
13 |
14 |
15 |
-------------------------------------------------------------------------------- /src/resources/views/podcasts/searchresults.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laravelpodcast::layouts.app') 2 | 3 | @section('template_title') 4 | Search Results 5 | @endsection 6 | 7 | @section('content') 8 | 9 | @if($items) 10 | @include('laravelpodcast::podcasts.player') 11 | @endif 12 | 13 |
14 |
15 |
16 |

17 | Search Results 18 |

19 |
20 |
21 |
22 |
23 |
24 | @if($items) 25 | @foreach ($items as $item) 26 | @include('laravelpodcast::podcasts.item') 27 | @endforeach 28 | @endif 29 |
30 |
31 |
32 | 33 | @endsection 34 | 35 | @section('footer-scripts') 36 | @include('laravelpodcast::scripts.podcast-scripts') 37 | @endsection -------------------------------------------------------------------------------- /src/resources/views/podcasts/settings.blade.php: -------------------------------------------------------------------------------- 1 | @extends('laravelpodcast::layouts.app') 2 | 3 | @section('template_title') 4 | Settings 5 | @endsection 6 | 7 | @section('content') 8 |
9 |
10 |
11 |

Settings

12 |
13 |
14 |
15 |
16 | @stop -------------------------------------------------------------------------------- /src/resources/views/scripts/add-modal-script.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/resources/views/scripts/delete-modal-script.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/resources/views/scripts/podcast-scripts.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/routes/web.php: -------------------------------------------------------------------------------- 1 | 'web'], function () { 16 | 17 | //Route::get('/', 'jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastsController@index'); 18 | 19 | Route::get('/podcast', 'jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastsController@index'); 20 | Route::get('/podcasts/player', 'jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastsController@index'); 21 | Route::get('/podcasts/manage', 'jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastsController@manage'); 22 | Route::get('/podcasts/favorites', 'jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastsController@favorites'); 23 | Route::get('/podcasts/settings', 'jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastsController@settings'); 24 | 25 | Route::get('/podcasts/auto-update', function () { 26 | $exitCode = Artisan::call('updatePodcastItems'); 27 | if ($exitCode == 0) { 28 | return redirect('podcasts/player'); 29 | } 30 | }); 31 | 32 | Route::resource('/podcasts', 'jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastsController'); 33 | 34 | Route::get('/podcast/search', 'jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastItemsController@search'); 35 | Route::post('/podcast/mark-as-read', 'jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastItemsController@markAsRead'); 36 | Route::post('/podcast/mark-as-favorite', 'jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastItemsController@markAsFavorite'); 37 | Route::post('/podcast/mark-all-prev-read', 'jeremykenedy\laravelpodcast\app\Http\Controllers\PodcastItemsController@markAllPrevAsRead'); 38 | }); 39 | --------------------------------------------------------------------------------