Merge branch 'podcast' into dev
This commit is contained in:
commit
c14c0149ec
18 changed files with 525 additions and 140 deletions
89
app/Console/Commands/CheckPodcastDownload.php
Normal file
89
app/Console/Commands/CheckPodcastDownload.php
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Podcast\CeleryTask;
|
||||||
|
use App\Models\Podcast\PodcastEpisode;
|
||||||
|
use App\Models\Podcast\ThirdPartyTrackReference;
|
||||||
|
use Celery\Celery;
|
||||||
|
use DateTime;
|
||||||
|
use DateTimeZone;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class CheckPodcastDownload extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'app:check-podcast-download';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Check for messages in celeryresults queue to update celery_tasks table for finished tasks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$tasks = CeleryTask::where('status','=','PENDING')->with('aThirdPartyTrackReferences')->get();
|
||||||
|
if ($tasks->count() > 0) {
|
||||||
|
foreach ($tasks as $task) {
|
||||||
|
Log::info($task->task_id);
|
||||||
|
$queue = 'celeryresults.'.$task['task_id'];
|
||||||
|
$c = new Celery(
|
||||||
|
host: config('rabbitmq.host'),
|
||||||
|
login: config('rabbitmq.user'),
|
||||||
|
password: config('rabbitmq.password'),
|
||||||
|
vhost: config('rabbitmq.vhost'),
|
||||||
|
exchange: 'celeryresults',
|
||||||
|
binding: $queue,
|
||||||
|
result_expire: 900000
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$message = $c->getAsyncResultMessage($task->name, $task->task_id);
|
||||||
|
|
||||||
|
if ($message) {
|
||||||
|
try {
|
||||||
|
DB::beginTransaction();
|
||||||
|
$body = json_decode($message['body'], true);
|
||||||
|
$result = json_decode($body['result'], true);
|
||||||
|
|
||||||
|
$podcastEpisode = PodcastEpisode::where('id', '=',$result['episodeid'])->first();
|
||||||
|
$podcastEpisode->fill([
|
||||||
|
'file_id' => $result['fileid'],
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
$timezone = new DateTimeZone(getenv('APP_TIMEZONE'));
|
||||||
|
$thirdPartyTrackReference = ThirdPartyTrackReference::where('foreign_id','=',$podcastEpisode->id)->first();
|
||||||
|
$thirdPartyTrackReference->fill([
|
||||||
|
'file_id' => $result['fileid'],
|
||||||
|
'upload_time' => new DateTime('now', $timezone)
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
$celeryTask = CeleryTask::where('task_id','=',$task->task_id)->where('track_reference','=',$thirdPartyTrackReference->id)->first();
|
||||||
|
$celeryTask->fill([
|
||||||
|
'status' => 'SUCCESS'
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('test '.$e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,9 @@ class Kernel extends ConsoleKernel
|
||||||
// $schedule->command('inspire')->hourly();
|
// $schedule->command('inspire')->hourly();
|
||||||
$schedule->command('telescope:prune --hours=48')->daily();
|
$schedule->command('telescope:prune --hours=48')->daily();
|
||||||
$schedule->command('app:create-show-schedule')->everyMinute();
|
$schedule->command('app:create-show-schedule')->everyMinute();
|
||||||
|
$schedule->command('app:check-podcast-download')->everyFiveSeconds();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,8 +7,10 @@ use App\Helpers\LengthFormatter;
|
||||||
use App\Lib\RabbitMQSender;
|
use App\Lib\RabbitMQSender;
|
||||||
use App\Models\File;
|
use App\Models\File;
|
||||||
use App\Models\TrackType;
|
use App\Models\TrackType;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class FileController extends Controller
|
class FileController extends Controller
|
||||||
|
@ -54,9 +56,19 @@ class FileController extends Controller
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
|
|
||||||
|
$apiKey = $request->header('php-auth-user');
|
||||||
|
|
||||||
//Accept request only from logged-in users
|
//Accept request only from logged-in users
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
throw new \Exception("You must be logged in");
|
if ($apiKey != 'some_secret_api_key') {
|
||||||
|
throw new \Exception("You must be logged in");
|
||||||
|
}
|
||||||
|
//ToDo: check how to work in Legacy, getting user in this way is quite horrible
|
||||||
|
try {
|
||||||
|
$user = User::where('type','=','P')->orderBy('id','ASC')->first();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Mime type list: https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
//Mime type list: https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
||||||
|
@ -138,6 +150,11 @@ class FileController extends Controller
|
||||||
$fields['track_type_id'] = TrackType::where('code','MUSICA')->first()->id;
|
$fields['track_type_id'] = TrackType::where('code','MUSICA')->first()->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Force BPM to int
|
||||||
|
if (isset($fields['bpm'])) {
|
||||||
|
$fields['bpm'] = intval($fields['bpm']);
|
||||||
|
}
|
||||||
|
|
||||||
//return json_encode(['req' => $fields,'id' => $id]);
|
//return json_encode(['req' => $fields,'id' => $id]);
|
||||||
|
|
||||||
$file->fill($fields)->save();
|
$file->fill($fields)->save();
|
||||||
|
|
|
@ -7,6 +7,7 @@ use App\Models\Podcast\Podcast;
|
||||||
use App\Models\Podcast\PodcastEpisode;
|
use App\Models\Podcast\PodcastEpisode;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class PodcastController extends Controller
|
class PodcastController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -53,12 +54,32 @@ class PodcastController extends Controller
|
||||||
return $xml->channel;
|
return $xml->channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getEpisodes(Request $request)
|
public function loadPodcastDataFromXml(Request $request) {
|
||||||
{
|
|
||||||
$xml = simplexml_load_file($request->url, null, LIBXML_NOCDATA);
|
try {
|
||||||
$xmlArray = (array) $xml->channel;
|
$xml = simplexml_load_file($request->url, null, LIBXML_NOCDATA);
|
||||||
//episodes are stored in `item` array
|
$xmlArray = (array) $xml->channel;
|
||||||
return $xmlArray['item'];
|
|
||||||
|
$newArray = [];
|
||||||
|
|
||||||
|
foreach ($xmlArray['item'] as $key => $item) {
|
||||||
|
$item = (array) $item;
|
||||||
|
$newArray[$key] = $item;
|
||||||
|
$downloading = PodcastEpisode::where('episode_guid','=',$item['guid'])->first();
|
||||||
|
if (!is_null($downloading)) {
|
||||||
|
$newArray[$key]['imported'] = $downloading->third_party_track_reference->celery_task->status;
|
||||||
|
} else {
|
||||||
|
$newArray[$key]['imported'] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$xmlArray = $newArray = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_encode([
|
||||||
|
'podcast' => $xmlArray,
|
||||||
|
'episodes' => $newArray
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function save(Request $request)
|
protected function save(Request $request)
|
||||||
|
@ -67,27 +88,40 @@ class PodcastController extends Controller
|
||||||
$xml = simplexml_load_file($request->url);
|
$xml = simplexml_load_file($request->url);
|
||||||
$itunes = $xml->channel->children('itunes', TRUE);
|
$itunes = $xml->channel->children('itunes', TRUE);
|
||||||
|
|
||||||
$dbPodcast = Podcast::firstOrNew(['id' => $request->id]);
|
try {
|
||||||
$dbPodcast->fill([
|
$dbPodcast = Podcast::firstOrNew(['id' => $request->id]);
|
||||||
'url' => $request->url,
|
DB::beginTransaction();
|
||||||
'title' => $xml->channel->title,
|
$dbPodcast->fill([
|
||||||
'creator' => $itunes->author,
|
'url' => $request->url,
|
||||||
'description' => $itunes->subtitle,
|
'title' => $request->title,
|
||||||
'language' => $xml->channel->language,
|
'creator' => $itunes->author,
|
||||||
'copyright' => $xml->channel->copyright,
|
'description' => $itunes->subtitle,
|
||||||
'link' => $xml->channel->link,
|
'language' => $xml->channel->language,
|
||||||
'itunes_author'=> $itunes->author,
|
'copyright' => $xml->channel->copyright,
|
||||||
'itunes_keywords' => '',
|
'link' => $xml->channel->link,
|
||||||
'itunes_summary' => $itunes->summary,
|
'itunes_author'=> $itunes->author,
|
||||||
'itunes_subtitle' => $itunes->subtitle,
|
'itunes_keywords' => '',
|
||||||
'itunes_category' => '',
|
'itunes_summary' => $itunes->summary,
|
||||||
'itunes_explicit' => $itunes->explicit,
|
'itunes_subtitle' => $itunes->subtitle,
|
||||||
'owner' => $user->id,
|
'itunes_category' => '',
|
||||||
])->save();
|
'itunes_explicit' => $itunes->explicit,
|
||||||
|
'owner' => $user->id,
|
||||||
|
])->save();
|
||||||
|
|
||||||
return response()->json([
|
$dbImportedPodcast = ImportedPodcast::firstOrNew(['podcast_id' => $dbPodcast->id]);;
|
||||||
'podcast' => $dbPodcast,
|
$dbImportedPodcast->fill([
|
||||||
'episodes' => $xml->channel->children('item', TRUE)
|
'auto_ingest' => !isset($request->auto_ingest) ? true : false,
|
||||||
]);
|
'podcast_id' => $dbPodcast->id
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'podcast' => $dbPodcast,
|
||||||
|
'episodes' => $xml->channel->children('item', TRUE)
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return response()->json(['message' => $e->getMessage()], 500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,16 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Podcast\CeleryTask;
|
||||||
use App\Models\Podcast\Podcast;
|
use App\Models\Podcast\Podcast;
|
||||||
use App\Models\Podcast\PodcastEpisode;
|
use App\Models\Podcast\PodcastEpisode;
|
||||||
|
use App\Models\Podcast\ThirdPartyTrackReference;
|
||||||
|
use Celery\Celery;
|
||||||
|
use DateTime;
|
||||||
|
use DateTimeZone;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Celery\Celery;
|
|
||||||
|
|
||||||
class PodcastEpisodeController extends Controller
|
class PodcastEpisodeController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -32,22 +36,6 @@ class PodcastEpisodeController extends Controller
|
||||||
if (!$user->id) {
|
if (!$user->id) {
|
||||||
throw new \Exception('You must be logged in');
|
throw new \Exception('You must be logged in');
|
||||||
}
|
}
|
||||||
return $this->downloadPodcastEpisode($request);
|
|
||||||
} catch (\Exception $exception) {
|
|
||||||
return response($exception->getMessage(), 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function downloadPodcastEpisode(Request $request) {
|
|
||||||
$request->validate([
|
|
||||||
'podcast_id' => 'required',
|
|
||||||
'episode_url' => 'required',
|
|
||||||
'episode_title' => 'required',
|
|
||||||
]);
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
$podcast = Podcast::findOrFail($request->podcast_id);
|
|
||||||
|
|
||||||
$podcastEpisode = new PodcastEpisode();
|
$podcastEpisode = new PodcastEpisode();
|
||||||
$podcastEpisode->fill([
|
$podcastEpisode->fill([
|
||||||
|
@ -59,6 +47,64 @@ class PodcastEpisodeController extends Controller
|
||||||
'episode_description' => htmlentities($request->episode['description']),
|
'episode_description' => htmlentities($request->episode['description']),
|
||||||
])->save();
|
])->save();
|
||||||
|
|
||||||
|
$brokerTask = $this->downloadPodcastEpisode($request, $podcastEpisode);
|
||||||
|
|
||||||
|
$ref = new ThirdPartyTrackReference();
|
||||||
|
$ref->fill([
|
||||||
|
'service' => 'podcast',
|
||||||
|
'foreign_id' => $podcastEpisode->id,
|
||||||
|
'file_id' => null
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
$task = new CeleryTask();
|
||||||
|
$timezone = new DateTimeZone(getenv('APP_TIMEZONE'));
|
||||||
|
$task->fill([
|
||||||
|
'task_id' => $brokerTask->task_id,
|
||||||
|
'track_reference' => $ref->id,
|
||||||
|
'name' => 'podcast-download',
|
||||||
|
'dispatch_time' => new DateTime('now', $timezone),
|
||||||
|
'status' => 'PENDING'
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
return $podcastEpisode->toJson();
|
||||||
|
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
return response($exception->getMessage(), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkPodcastEpisodeDownload($id) {
|
||||||
|
$episode = PodcastEpisode::where('id',$id)->firstOrFail();
|
||||||
|
return $episode->third_party_track_reference->celery_task->status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function savePodcastEpisode(Request $request) {
|
||||||
|
|
||||||
|
if(isset($request->file)) {
|
||||||
|
try {
|
||||||
|
$file = (new FileController())->store($request);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return response($e->getMessage(), 500);
|
||||||
|
}
|
||||||
|
if ($file) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
return json_encode([
|
||||||
|
'id' => 0
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function downloadPodcastEpisode(Request $request, $podcastEpisode) {
|
||||||
|
$request->validate([
|
||||||
|
'podcast_id' => 'required',
|
||||||
|
'episode_url' => 'required',
|
||||||
|
'episode_title' => 'required',
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$podcast = Podcast::findOrFail($request->podcast_id);
|
||||||
|
|
||||||
$conn = new Celery(
|
$conn = new Celery(
|
||||||
config('rabbitmq.host'),
|
config('rabbitmq.host'),
|
||||||
config('rabbitmq.user'),
|
config('rabbitmq.user'),
|
||||||
|
@ -79,24 +125,9 @@ class PodcastEpisodeController extends Controller
|
||||||
'override_album' => 'false' //ToDo connect $album_override from imported_podcast,
|
'override_album' => 'false' //ToDo connect $album_override from imported_podcast,
|
||||||
];
|
];
|
||||||
|
|
||||||
$result = $conn->PostTask('podcast-download', $data, true, 'podcast');
|
$taskId = $conn->PostTask('podcast-download', $data, true, 'podcast');
|
||||||
|
return $taskId;
|
||||||
|
|
||||||
return $result->getId();
|
|
||||||
|
|
||||||
while (!$result->isReady()) {
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!$result->isSuccess()) {
|
|
||||||
//Todo: throw exception
|
|
||||||
throw new \Exception('podcast episode id:'.$podcastEpisode->id.' download failed');
|
|
||||||
}
|
|
||||||
//Todo: return ok
|
|
||||||
return $result;
|
|
||||||
// $podcastEpisode->fill([
|
|
||||||
// ''
|
|
||||||
// ]);
|
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
Log::error($exception->getMessage());
|
Log::error($exception->getMessage());
|
||||||
die($exception->getMessage());
|
die($exception->getMessage());
|
||||||
|
|
27
app/Models/Podcast/CeleryTask.php
Normal file
27
app/Models/Podcast/CeleryTask.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Podcast;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class CeleryTask extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'celery_tasks';
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'task_id',
|
||||||
|
'track_reference',
|
||||||
|
'name',
|
||||||
|
'dispatch_time',
|
||||||
|
'status'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function aThirdPartyTrackReferences() {
|
||||||
|
return $this->hasOne(ThirdPartyTrackReference::class, 'id', 'track_reference');
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,4 +29,8 @@ class PodcastEpisode extends Model
|
||||||
public function podcast() {
|
public function podcast() {
|
||||||
return $this->belongsTo(Podcast::class);
|
return $this->belongsTo(Podcast::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function third_party_track_reference() {
|
||||||
|
return $this->hasOne(ThirdPartyTrackReference::class, 'foreign_id', 'id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
36
app/Models/Podcast/ThirdPartyTrackReference.php
Normal file
36
app/Models/Podcast/ThirdPartyTrackReference.php
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Podcast;
|
||||||
|
|
||||||
|
use App\Models\File;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class ThirdPartyTrackReference extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'third_party_track_references';
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'service',
|
||||||
|
'foreign_id',
|
||||||
|
'file_id',
|
||||||
|
'upload_time',
|
||||||
|
'status'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function file() {
|
||||||
|
return $this->hasOne(File::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function podcast_episode() {
|
||||||
|
return $this->belongsTo(PodcastEpisode::class, 'foreign_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function celery_task() {
|
||||||
|
return $this->hasOne(CeleryTask::class, 'track_reference');
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,8 +18,15 @@
|
||||||
"mxl/laravel-job": "^1.6",
|
"mxl/laravel-job": "^1.6",
|
||||||
"php-amqplib/php-amqplib": "^3.7",
|
"php-amqplib/php-amqplib": "^3.7",
|
||||||
"spatie/laravel-permission": "^6.13",
|
"spatie/laravel-permission": "^6.13",
|
||||||
"xenos/musicbrainz": "^1.0"
|
"xenos/musicbrainz": "^1.0",
|
||||||
|
"libretime/celery-php": "dev-main"
|
||||||
},
|
},
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "vcs",
|
||||||
|
"url": "https://github.com/libretime/celery-php.git"
|
||||||
|
}
|
||||||
|
],
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.9.1",
|
"fakerphp/faker": "^1.9.1",
|
||||||
"laravel/breeze": "*",
|
"laravel/breeze": "*",
|
||||||
|
|
|
@ -14,6 +14,7 @@ podcastStore.loadPodcast(basePodcast());
|
||||||
const { items, listData, headers, selected, loading, search, getItems, editItem, deleteItem } = podcast_page();
|
const { items, listData, headers, selected, loading, search, getItems, editItem, deleteItem } = podcast_page();
|
||||||
const url = ref('');
|
const url = ref('');
|
||||||
const itemEdited = ref(basePodcast());
|
const itemEdited = ref(basePodcast());
|
||||||
|
const episodes = ref([]);
|
||||||
const bulk = ref(false);
|
const bulk = ref(false);
|
||||||
const dialog = reactive({
|
const dialog = reactive({
|
||||||
open: false,
|
open: false,
|
||||||
|
@ -21,6 +22,7 @@ const dialog = reactive({
|
||||||
title: '',
|
title: '',
|
||||||
text: ''
|
text: ''
|
||||||
});
|
});
|
||||||
|
const dialogLoading = ref(false);
|
||||||
|
|
||||||
const openDialog = (type, title = '', text = '', bulk = false) => {
|
const openDialog = (type, title = '', text = '', bulk = false) => {
|
||||||
dialog.open = true
|
dialog.open = true
|
||||||
|
@ -47,12 +49,29 @@ const confirm = (confirm, bulk) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmAdd = (confirm) => {
|
const confirmAdd = async (confirm) => {
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
podcastStore.updateField({key: 'url', value: url});
|
dialogLoading.value = true;
|
||||||
console.log(podcastStore);
|
await axios.get('/rss_podcast_load', {
|
||||||
|
params: {
|
||||||
|
url: url.value,
|
||||||
|
}
|
||||||
|
}).then(res => {
|
||||||
|
if (res.data.podcast) {
|
||||||
|
podcastStore.updateField({key: 'title', value: res.data.podcast.title});
|
||||||
|
podcastStore.updateField({key: 'url', value: url});
|
||||||
|
podcastStore.currentPodcastEpisodes = res.data.episodes;
|
||||||
|
}
|
||||||
|
closeDialog();
|
||||||
|
dialogLoading.value = false;
|
||||||
|
//episodes.value = res.data.episodes;
|
||||||
|
openDialog(
|
||||||
|
'info',
|
||||||
|
'Attenzione',
|
||||||
|
'L\'URL inserito non è un feed RSS valido'
|
||||||
|
);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
closeDialog()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const edit = (item) => {
|
const edit = (item) => {
|
||||||
|
@ -60,42 +79,46 @@ const edit = (item) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const cancel = (item) => {
|
const cancel = (item) => {
|
||||||
bulk.value = Array.isArray(item)
|
bulk.value = Array.isArray(item);
|
||||||
itemEdited.value = item
|
itemEdited.value = item;
|
||||||
openDialog(
|
openDialog(
|
||||||
'delete',
|
'delete',
|
||||||
'Cancella',
|
'Cancella',
|
||||||
bulk.value ? 'Vuoi cancellare i podcast selezionati?' : 'Vuoi cancellare il podcast selezionato?'
|
bulk.value ? 'Vuoi cancellare i podcast selezionati?' : 'Vuoi cancellare il podcast selezionato?'
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmDelete = (confirm, bulk) => {
|
const confirmDelete = (confirm, bulk) => {
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
if (!bulk) {
|
if (!bulk) {
|
||||||
deleteItem(itemEdited.value.id)
|
deleteItem(itemEdited.value.id);
|
||||||
} else {
|
} else {
|
||||||
itemEdited.value.forEach(el => {
|
itemEdited.value.forEach(el => {
|
||||||
deleteItem(el.id)
|
deleteItem(el.id);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closeDialog()
|
closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeDialog = () => {
|
const closeDialog = () => {
|
||||||
dialog.open = false
|
dialog.open = false;
|
||||||
itemEdited.value = basePodcast();
|
itemEdited.value = basePodcast();
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateSearch = (text) => {
|
const updateSearch = (text) => {
|
||||||
search.value = text
|
search.value = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetItemEdited = () => {
|
||||||
|
podcastStore.currentPodcast = basePodcast();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PodcastEditor
|
<PodcastEditor
|
||||||
v-if="podcastStore.currentPodcast.url != '' && podcastStore.currentPodcast.url != null"
|
v-if="podcastStore.currentPodcast.url != '' && podcastStore.currentPodcast.url != null"
|
||||||
@go-back="podcastStore.currentPodcast = basePodcast()"
|
@go-back="resetItemEdited"
|
||||||
/>
|
/>
|
||||||
<Table
|
<Table
|
||||||
v-else
|
v-else
|
||||||
|
@ -126,6 +149,8 @@ const updateSearch = (text) => {
|
||||||
:bulk="bulk"
|
:bulk="bulk"
|
||||||
@confirm="confirm"
|
@confirm="confirm"
|
||||||
@after-leave="closeDialog"
|
@after-leave="closeDialog"
|
||||||
|
:loading="dialogLoading"
|
||||||
|
:hide_confirm="dialog.type === 'info' ? true : false"
|
||||||
>
|
>
|
||||||
<VTextField
|
<VTextField
|
||||||
label="Feed RSS"
|
label="Feed RSS"
|
||||||
|
|
|
@ -3,57 +3,111 @@ import {useAuthStore} from "@/stores/auth.store.ts";
|
||||||
import {usePodcastStore} from "@/stores/podcast.store.ts";
|
import {usePodcastStore} from "@/stores/podcast.store.ts";
|
||||||
import {podcast} from "@models/podcast/podcast.ts";
|
import {podcast} from "@models/podcast/podcast.ts";
|
||||||
import {podcast_episode_page} from "@/composables/content/podcastEpisode_page.ts";
|
import {podcast_episode_page} from "@/composables/content/podcastEpisode_page.ts";
|
||||||
import {ref, watch} from "vue";
|
import {onBeforeMount, reactive, ref, watch} from "vue";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import ConfirmDelete from "@partials/dialogs/ConfirmDelete.vue";
|
||||||
|
import {podcast_page} from "@/composables/content/podcast_page.ts";
|
||||||
|
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
'saveItem'
|
'saveItem',
|
||||||
|
'goBack'
|
||||||
])
|
])
|
||||||
|
|
||||||
const podcastStore = usePodcastStore();
|
const podcastStore = usePodcastStore();
|
||||||
const item = podcastStore.currentPodcast;
|
const item = podcastStore.currentPodcast;
|
||||||
|
|
||||||
|
const { items, headers, loading, downloadEpisode, getItems } = podcast_episode_page(item.url);
|
||||||
|
|
||||||
|
const podcast_id = ref(item.id);
|
||||||
console.log(item)
|
console.log(item)
|
||||||
|
|
||||||
const podcastFields = podcast(item);
|
const podcastFields = podcast(item);
|
||||||
const { items, headers, loading, downloadingEpisode, downloadEpisode } = podcast_episode_page(item.id, item.url);
|
console.log(podcastFields())
|
||||||
|
const { editItem } = podcast_page();
|
||||||
|
|
||||||
const episodes = ref([]);
|
const episodes = ref([]);
|
||||||
|
|
||||||
|
const disabledSaveButton = ref(false)
|
||||||
|
|
||||||
|
const dialog = reactive({
|
||||||
|
open: false,
|
||||||
|
type: '',
|
||||||
|
title: '',
|
||||||
|
text: '',
|
||||||
|
item: null
|
||||||
|
})
|
||||||
|
|
||||||
|
const openDialog = (type, title = '', text = '', item = null) => {
|
||||||
|
dialog.open = true
|
||||||
|
dialog.type = type
|
||||||
|
dialog.title = title
|
||||||
|
dialog.text = text
|
||||||
|
dialog.item = item
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeDialog = () => {
|
||||||
|
dialog.open = false
|
||||||
|
}
|
||||||
|
|
||||||
const checkError = (field, model) => {
|
const checkError = (field, model) => {
|
||||||
if (field.required) {
|
if (field.required) {
|
||||||
// const error = field.required && (model === '' || model === null)
|
const error = field.required && (model === '' || model === null)
|
||||||
// // disabledSaveButton.value = error
|
disabledSaveButton.value = error
|
||||||
// return error
|
return error
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(items, (newItems, oldItems) => {
|
const checkDownload = (item) => {
|
||||||
console.log(newItems, oldItems)
|
if (podcast_id.value > 0) {
|
||||||
if (item.id > 0) {
|
// console.log(item);
|
||||||
axios.get('/podcast_episode', {
|
downloadEpisode(podcast_id.value, item);
|
||||||
params: {
|
} else {
|
||||||
podcast_id: item.id
|
openDialog(
|
||||||
}
|
'save',
|
||||||
}).then((response) => {
|
'Salvataggio necessario',
|
||||||
newItems.forEach((element) => {
|
'Per procedere con il download dell\'episodio è necessario salvare il podcast. Confermi?',
|
||||||
const episode = response.data.filter(imp => {
|
item
|
||||||
if (imp.episode_guid === element.guid) {
|
)
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (episode.length > 0) {
|
|
||||||
element.imported = (episode[0].file_id === null) ? null : episode[0].file_id;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
console.log(newItems)
|
}
|
||||||
|
|
||||||
|
const confirmSave = (confirm, bulk, row) => {
|
||||||
|
console.log(confirm, row)
|
||||||
|
if (confirm) {
|
||||||
|
save(row);
|
||||||
|
}
|
||||||
|
closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = (row) => {
|
||||||
|
console.log(row)
|
||||||
|
// const errors
|
||||||
|
editItem(item).then(res => {
|
||||||
|
podcastStore.loadPodcast(res.podcast);
|
||||||
|
podcast_id.value = res.podcast.id;
|
||||||
|
console.log(podcast_id.value);
|
||||||
|
//Check if row is effectively a podcast episode object using his `title` property
|
||||||
|
if (row.title) {
|
||||||
|
downloadEpisode(podcast_id.value, row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(items, (newItems, oldItems) => {
|
||||||
episodes.value = newItems;
|
episodes.value = newItems;
|
||||||
}, {deep: true});
|
}, {deep: true});
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
getItems(false, item.id);
|
||||||
|
}, 5000)
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
getItems().then(title => podcastStore.updateField({'title': title}));
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -66,7 +120,6 @@ watch(items, (newItems, oldItems) => {
|
||||||
:value="field.value ? field.value : field.type == 'checkbox' ? true : null"
|
:value="field.value ? field.value : field.type == 'checkbox' ? true : null"
|
||||||
:disabled="field.disabled"
|
:disabled="field.disabled"
|
||||||
@update:modelValue="checkError(field, item[key])"
|
@update:modelValue="checkError(field, item[key])"
|
||||||
@update-property="updateProperty"
|
|
||||||
:error="checkError(field, item[key])"
|
:error="checkError(field, item[key])"
|
||||||
rows="2"
|
rows="2"
|
||||||
:items="field.items"
|
:items="field.items"
|
||||||
|
@ -100,26 +153,25 @@ watch(items, (newItems, oldItems) => {
|
||||||
{{ item.short_description }}
|
{{ item.short_description }}
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:item.imported="{ item }">
|
<template v-slot:item.imported="{ item }">
|
||||||
{{item.imported}}
|
|
||||||
<v-icon
|
<v-icon
|
||||||
class="me-2 spinning"
|
class="me-2 spinning"
|
||||||
size="small"
|
size="small"
|
||||||
v-if="item.imported === null"
|
v-if="item.imported === 'PENDING'"
|
||||||
>
|
>
|
||||||
mdi-loading
|
mdi-loading
|
||||||
</v-icon>
|
</v-icon>
|
||||||
<v-icon
|
<v-icon
|
||||||
class="me-2"
|
class="me-2"
|
||||||
size="small"
|
size="small"
|
||||||
v-else-if="item.imported > 0"
|
v-else-if="item.imported === 'SUCCESS'"
|
||||||
>
|
>
|
||||||
mdi-check-outline
|
mdi-check-outline
|
||||||
</v-icon>
|
</v-icon>
|
||||||
<v-icon
|
<v-icon
|
||||||
class="me-2 text-center"
|
class="me-2 text-center"
|
||||||
size="small"
|
size="small"
|
||||||
v-else
|
v-else-if="item.imported === 0"
|
||||||
@click="downloadEpisode(item)"
|
@click="checkDownload(item)"
|
||||||
>
|
>
|
||||||
mdi-download-box
|
mdi-download-box
|
||||||
</v-icon>
|
</v-icon>
|
||||||
|
@ -127,6 +179,17 @@ watch(items, (newItems, oldItems) => {
|
||||||
</VDataTable>
|
</VDataTable>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
<v-dialog v-model="dialog.open">
|
||||||
|
<ConfirmDelete
|
||||||
|
v-if="dialog.type === 'save'"
|
||||||
|
:title="dialog.title"
|
||||||
|
:text="dialog.text"
|
||||||
|
:bulk="false"
|
||||||
|
:item="dialog.item"
|
||||||
|
@confirm="confirmSave"
|
||||||
|
@after-leave="closeDialog"
|
||||||
|
/>
|
||||||
|
</v-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -2,10 +2,30 @@
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: String,
|
title: String,
|
||||||
text: String,
|
text: String,
|
||||||
bulk: Boolean
|
confirm_text: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
cancel_text: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
bulk: Boolean,
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
hide_confirm: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(props.bulk)
|
console.log(props.loading)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -20,7 +40,9 @@ console.log(props.bulk)
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-btn
|
<v-btn
|
||||||
color="primary"
|
color="primary"
|
||||||
@click="$emit('confirm',true, props.bulk)"
|
@click="$emit('confirm',true, props.bulk, item)"
|
||||||
|
:loading="loading"
|
||||||
|
v-if="hide_confirm != true"
|
||||||
>Conferma</v-btn>
|
>Conferma</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
color="accent"
|
color="accent"
|
||||||
|
|
|
@ -70,7 +70,7 @@ export const deletePodcast = async (podcastIds: Number[]) => {
|
||||||
|
|
||||||
export function podcast(item) {
|
export function podcast(item) {
|
||||||
const visibleFields = {
|
const visibleFields = {
|
||||||
name: {
|
title: {
|
||||||
title: 'Nome del podcast',
|
title: 'Nome del podcast',
|
||||||
required: true,
|
required: true,
|
||||||
disabled: false
|
disabled: false
|
||||||
|
|
|
@ -2,8 +2,9 @@ import {reactive, ref} from "vue";
|
||||||
import axios, {type AxiosResponse} from "axios";
|
import axios, {type AxiosResponse} from "axios";
|
||||||
import {type PodcastEpisode, PodcastEpisodeTableHeader} from "@models/podcast/podcastEpisode.ts";
|
import {type PodcastEpisode, PodcastEpisodeTableHeader} from "@models/podcast/podcastEpisode.ts";
|
||||||
import {DateTime} from "luxon";
|
import {DateTime} from "luxon";
|
||||||
|
import {usePodcastStore} from "@/stores/podcast.store.ts";
|
||||||
|
|
||||||
export function podcast_episode_page(podcast_id: Number, url: String) {
|
export function podcast_episode_page(url: String) {
|
||||||
const items = ref([]);
|
const items = ref([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const listData = reactive({
|
const listData = reactive({
|
||||||
|
@ -35,40 +36,44 @@ export function podcast_episode_page(podcast_id: Number, url: String) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const getItems = async () => {
|
const getItems = async (load, podcast_id = 0) => {
|
||||||
loading.value = true;
|
if (load) {
|
||||||
console.log(url)
|
loading.value = true;
|
||||||
return await axios.get(`/rss_podcast_episodes`, {
|
}
|
||||||
|
return await axios.get(`/rss_podcast_load`, {
|
||||||
params: {
|
params: {
|
||||||
podcast_id: podcast_id,
|
|
||||||
url: url
|
url: url
|
||||||
}
|
}
|
||||||
}).then( async (podcastEpisodesList: AxiosResponse) => {
|
}).then( async (podcastEpisodesList: AxiosResponse) => {
|
||||||
// console.log(podcastEpisodesList, podcast_id);
|
const episodes = podcastEpisodesList.data.episodes;
|
||||||
const episodes = podcastEpisodesList.data;
|
items.value = episodes.map(element => {
|
||||||
items.value = await episodes.map(element => {
|
//element.imported = -1;
|
||||||
element.imported = -1;
|
|
||||||
element.short_description = '';
|
element.short_description = '';
|
||||||
const arr = element.description.split(' ');
|
if (typeof element.description === "string") {
|
||||||
for (let j = 0; j < 20; j++) {
|
const arr = element.description.split(' ');
|
||||||
element.short_description += arr[j];
|
const limit = arr.length >= 20 ? 20 : arr.length;
|
||||||
element.short_description += (j === 19) ? '...' : ' ';
|
for (let j = 0; j < limit; j++) {
|
||||||
|
element.short_description += arr[j];
|
||||||
|
element.short_description += (j === 19) ? '...' : ' ';
|
||||||
|
}
|
||||||
|
element.short_description = (element.short_description + '').replace(/&#\d+;/gm, function(s) {
|
||||||
|
return String.fromCharCode(s.match(/\d+/gm)[0]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
element.short_description = (element.short_description + '').replace(/&#\d+;/gm, function(s) {
|
|
||||||
return String.fromCharCode(s.match(/\d+/gm)[0]);
|
|
||||||
});
|
|
||||||
return element;
|
return element;
|
||||||
});
|
});
|
||||||
loading.value = false;
|
|
||||||
|
if (load) {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return podcastEpisodesList.data.podcast.title;
|
||||||
}).catch((error: Error) => {
|
}).catch((error: Error) => {
|
||||||
console.log("Error: " + error);
|
console.log("Error: " + error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadEpisode = async (item) => {
|
const downloadEpisode = async (podcast_id, item) => {
|
||||||
console.log(item.enclosure["@attributes"].url)
|
|
||||||
return await axios.post('/podcast_episode', {
|
return await axios.post('/podcast_episode', {
|
||||||
podcast_id: podcast_id,
|
podcast_id: podcast_id,
|
||||||
episode: item,
|
episode: item,
|
||||||
|
@ -80,7 +85,21 @@ export function podcast_episode_page(podcast_id: Number, url: String) {
|
||||||
console.log("Error: "+error);
|
console.log("Error: "+error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getItems();
|
|
||||||
|
|
||||||
return { items, listData, headers, loading, downloadingEpisode, downloadEpisode }
|
const checkDownloadEpisode = async (episode_id) => {
|
||||||
|
return await axios.get(`/check_podcast_episode_download/${episode_id}`, {
|
||||||
|
params: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}).then( (response) => {
|
||||||
|
if (response.data === 'SUCCESS') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
checkDownloadEpisode(episode_id);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getItems(true);
|
||||||
|
|
||||||
|
return { items, listData, headers, loading, downloadingEpisode, getItems, downloadEpisode }
|
||||||
}
|
}
|
|
@ -45,9 +45,9 @@ watch(currentPage, (newVal) => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-col>
|
<v-col>
|
||||||
<!-- <keep-alive>-->
|
<!-- <keep-alive>-->
|
||||||
<Component :is="tabs[currentPage]" />
|
<Component :is="tabs[currentPage]" />
|
||||||
<!-- </keep-alive>-->
|
<!-- </keep-alive>-->
|
||||||
</v-col>
|
</v-col>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import type {PodcastEpisode} from "@models/podcast/podcastEpisode.ts";
|
||||||
export const usePodcastStore = defineStore('podcast', {
|
export const usePodcastStore = defineStore('podcast', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
currentPodcast: {} as Podcast,
|
currentPodcast: {} as Podcast,
|
||||||
currentPodcastEpisodes: {} as PodcastEpisode,
|
currentPodcastEpisodes: {} as PodcastEpisode[],
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
loadPodcast(podcastData: Podcast) {
|
loadPodcast(podcastData: Podcast) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Http\Controllers\FileController;
|
use App\Http\Controllers\FileController;
|
||||||
|
use App\Http\Controllers\PodcastEpisodeController;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Http\Controllers\TestControllerXdebug;
|
use App\Http\Controllers\TestControllerXdebug;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
@ -16,4 +17,5 @@ use Illuminate\Support\Facades\Route;
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Route::middleware('parsejson')->put('/media/{id}', [FileController::class, 'update'])->name('media.update');
|
Route::middleware('parsejson')->put('/media/{id}', [FileController::class, 'update'])->name('media.update');
|
||||||
|
Route::post('/media', [PodcastEpisodeController::class, 'savePodcastEpisode'])->name('media.podcast');
|
||||||
|
|
|
@ -57,6 +57,12 @@ Route::get('/file/check_upload_status/{id}', [FileController::class, 'checkUploa
|
||||||
*/
|
*/
|
||||||
Route::post('/smartblock/{id}/tracks', [SmartBlockController::class, 'getTrackList']);
|
Route::post('/smartblock/{id}/tracks', [SmartBlockController::class, 'getTrackList']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Podcast and podcast episodes custom routes
|
||||||
|
*/
|
||||||
|
Route::get('/rss_podcast_load', [PodcastController::class, 'loadPodcastDataFromXml']);
|
||||||
|
Route::get('/check_podcast_episode_download/{id}', [PodcastEpisodeController::class, 'checkPodcastEpisodeDownload']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preferences custom routes
|
* Preferences custom routes
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue