Skip to content

Commit

Permalink
Merge pull request #68 from inovector/develop
Browse files Browse the repository at this point in the history
Unsplash trigger download Job & Media improvements
  • Loading branch information
lao9s authored Dec 2, 2023
2 parents 83049a7 + d457a61 commit 9af04b4
Show file tree
Hide file tree
Showing 17 changed files with 267 additions and 36 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"php": "^8.1",
"ext-fileinfo": "*",
"abraham/twitteroauth": "^4.0",
"guzzlehttp/guzzle": "^7.5",
"illuminate/contracts": "^9.28|^10.0",
"inertiajs/inertia-laravel": "^0.6.9",
"intervention/image": "^2.7",
Expand Down
2 changes: 1 addition & 1 deletion config/mixpost.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
* The media component is integrated with third-party services Unsplash.com and Tenor.com
* Defines the default terms for displaying media resources
*/
'external_media_terms' => ['young', 'social', 'mix', 'content', 'viral', 'trend', 'test', 'light', 'true', 'false', 'marketing', 'self-hosted', 'ambient', 'writer', 'technology'],
'external_media_terms' => ['social', 'mix', 'content', 'popular', 'viral', 'trend', 'light', 'marketing', 'self-hosted', 'ambient', 'writer', 'technology'],

/*
* Options for each social network
Expand Down
5 changes: 4 additions & 1 deletion resources/js/Components/Media/AddMedia.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ const insert = () => {
if (toDownload) {
// Download external media files
downloadExternal(selectedItems.value, (response) => {
downloadExternal(selectedItems.value.map((item) => {
const {id, url, download_data} = item;
return {id, url, download_data};
}), (response) => {
emit('insert', response.data);
close();
})
Expand Down
2 changes: 1 addition & 1 deletion resources/js/Components/Media/MediaCredit.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="absolute z-10 bottom-0 mb-xs left-0 ml-xs hidden group-hover:block">
<div class="absolute z-10 bottom-0 mb-xs left-0 ml-xs hidden group-hover:block shadow-mix">
<div class="bg-white shadow-mix py-xs px-sm text-left rounded-md"><slot/></div>
</div>
</template>
9 changes: 2 additions & 7 deletions resources/js/Components/Media/MediaFile.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script setup>
import {startsWith} from "lodash";
import {computed} from "vue";
import ExclamationCircleIcon from "@/Icons/ExclamationCircle.vue"
import VideoSolidIcon from "@/Icons/VideoSolid.vue"
Expand All @@ -21,10 +20,6 @@ const imgHeightClass = computed(() => {
'sm': 'h-20'
}[props.imgHeight]
})
const isVideo = computed(() => {
return startsWith(props.media.mime_type, 'video');
})
</script>
<template>
<figure class="relative">
Expand All @@ -33,7 +28,7 @@ const isVideo = computed(() => {
class="relative flex rounded"
:class="{'border border-red-500 p-5': media.hasOwnProperty('error')}"
>
<span v-if="isVideo" class="absolute top-0 right-0 mt-1 mr-1">
<span v-if="media.is_video" class="absolute top-0 right-0 mt-1 mr-1">
<VideoSolidIcon class="!w-4 !h-4 text-white"/>
</span>

Expand All @@ -47,7 +42,7 @@ const isVideo = computed(() => {
:src="media.thumb_url"
loading="lazy"
alt="Image"
class="w-auto object-cover rounded-md"
class="w-full rounded-md"
:class="imgHeightClass"
/>
</div>
Expand Down
14 changes: 12 additions & 2 deletions resources/js/Components/Media/MediaStock.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup>
import {computed, onMounted} from "vue";
import {usePage, Link} from "@inertiajs/vue3";
import {snakeCase} from "lodash";
import useMedia from "@/Composables/useMedia";
import MediaSelectable from "@/Components/Media/MediaSelectable.vue";
import MediaFile from "@/Components/Media/MediaFile.vue";
Expand All @@ -18,6 +19,10 @@ const props = defineProps({
}
})
const appName = computed(() => {
return snakeCase(usePage().props.app.name);
})
const enabled = computed(() => {
return usePage().props.is_configured_service.unsplash;
})
Expand Down Expand Up @@ -53,8 +58,13 @@ defineExpose({selected, deselectAll})
<MediaSelectable v-if="item" :active="isSelected(item)" @click="toggleSelect(item)">
<MediaFile :media="item" class="group">
<MediaCredit>
<div>Image from Unsplash</div>
<div>By <a :href="item.credit_url" target="_blank" class="link">{{ item.name }}</a></div>
<div>Image from <a
:href="`https://unsplash.com/?utm_source=${appName}&utm_medium=referral`"
target="_blank" class="link">Unsplash</a>
</div>
<div>By <a :href="`${item.credit_url}?utm_source=${appName}&utm_medium=referral`"
target="_blank" class="link">{{ item.name }}</a>
</div>
</MediaCredit>
</MediaFile>
</MediaSelectable>
Expand Down
3 changes: 2 additions & 1 deletion resources/js/Composables/useMedia.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ const useMedia = (routeName = 'mixpost.media.fetchUploads', routeParams = {}) =>
NProgress.start();

axios.post(route('mixpost.media.download', routeParams), {
items
items,
from: activeTab.value,
}).then((response) => {
callback(response);
}).catch(() => {
Expand Down
5 changes: 4 additions & 1 deletion resources/js/Pages/Media.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ const use = () => {
const toDownload = activeTab.value !== 'uploads';
if (toDownload) {
downloadExternal(selectedItems.value, (response) => {
downloadExternal(selectedItems.value.map((item) => {
const {id, url, download_data} = item;
return {id, url, download_data};
}), (response) => {
createPost(response.data);
});
}
Expand Down
1 change: 1 addition & 0 deletions src/Http/Controllers/MediaFetchGifsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public function __invoke(Request $request): AnonymousResourceCollection
]);

$media->setAttribute('id', $item['id']);
$media->setAttribute('download_data', 'false');

return $media;
});
Expand Down
25 changes: 7 additions & 18 deletions src/Http/Controllers/MediaFetchStockController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,19 @@
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Routing\Controller;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Http;
use Inovector\Mixpost\Facades\Services;
use Inovector\Mixpost\Http\Resources\MediaResource;
use Inovector\Mixpost\Integrations\Unsplash\Unsplash;
use Inovector\Mixpost\Models\Media;
use Symfony\Component\HttpFoundation\Response;

class MediaFetchStockController extends Controller
{
public function __invoke(Request $request): AnonymousResourceCollection
{
$clientId = Services::get('unsplash', 'client_id');
$unsplash = new Unsplash();

if (!$clientId) {
abort(Response::HTTP_FORBIDDEN);
}
$items = $unsplash->photos($request->query('keyword', ''), $request->query('page', 1));

$terms = config('mixpost.external_media_terms');

$items = Http::get("https://api.unsplash.com/search/photos", [
'client_id' => $clientId,
'query' => $request->query('keyword', Arr::random($terms)),
'page' => $request->query('page', 1),
'per_page' => 30,
]);

$media = collect($items->json('results', []))->map(function ($item) {
$media = collect($items)->map(function ($item) {
$media = new Media([
'name' => $item['user']['name'],
'mime_type' => 'image/jpeg',
Expand All @@ -48,6 +34,9 @@ public function __invoke(Request $request): AnonymousResourceCollection

$media->setAttribute('id', $item['id']);
$media->setAttribute('credit_url', $item['user']['links']['html']);
$media->setAttribute('download_data', [
'download_location' => $item['links']['download_location']
]);

return $media;
});
Expand Down
6 changes: 5 additions & 1 deletion src/Http/Middleware/HandleInertiaRequests.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Composer\InstalledVersions;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Inertia\Middleware;
use Inovector\Mixpost\Concerns\UsesAuth;
use Inovector\Mixpost\Facades\Settings;
Expand Down Expand Up @@ -60,10 +61,13 @@ public function share(Request $request)
'info' => $request->session()->get('info'),
];
},
'app' => [
'name' => Config::get('app.name')
],
'mixpost' => [
'docs_link' => 'https://docs.inovector.com',
'version' => InstalledVersions::getVersion('inovector/mixpost'),
'mime_types' => config('mixpost.mime_types'),
'mime_types' => Config::get('mixpost.mime_types'),
'settings' => [
'timezone' => Settings::get('timezone'),
'time_format' => Settings::get('time_format'),
Expand Down
54 changes: 52 additions & 2 deletions src/Http/Requests/MediaDownloadExternal.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,47 @@

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Inovector\Mixpost\Integrations\Unsplash\Jobs\TriggerDownloadJob;
use Inovector\Mixpost\MediaConversions\MediaImageResizeConversion;
use Inovector\Mixpost\Support\File;
use Illuminate\Support\Facades\Http;
use Inovector\Mixpost\Support\MediaUploader;
use Inovector\Mixpost\Util;

class MediaDownloadExternal extends FormRequest
{
public function rules(): array
{
return [
'items' => ['required', 'array']
'from' => ['required', 'string', 'in:stock,gifs'],
'items' => [
'required',
'array',
function ($attribute, $value, $fail) {
foreach ($value as $item) {
$validKeys = ['id', 'url', 'download_data'];

$extraKeys = array_diff(array_keys($item), $validKeys);

if (!empty($extraKeys)) {
$fail('The ' . $attribute . ' item contains invalid keys: ' . implode(', ', $extraKeys));
break;
}

foreach ($validKeys as $key) {
if (empty($item[$key])) {
$fail('The ' . $attribute . ' item must have a non-empty "' . $key . '" key.');
break 2;
}
}

if (!Util::isPublicDomainUrl($item['url'])) {
$fail('The ' . $attribute . ' contains non-public domain URLs.');
}
}
},
],
];
}

Expand All @@ -27,9 +57,29 @@ public function handle(): Collection

$file = File::fromBase64(base64_encode($result->body()));

return MediaUploader::fromFile($file)->path("mixpost/$now")->conversions([
$media = MediaUploader::fromFile($file)->path("mixpost/$now")->conversions([
MediaImageResizeConversion::name('thumb')->width(430),
])->uploadAndInsert();

$method = 'downloadAction' . Str::studly($this->input('from'));

$this->$method($item);

return $media;
});
}

protected function downloadActionStock(array $item): void
{
if (empty($item['download_data']['download_location'])) {
return;
}

TriggerDownloadJob::dispatch($item['download_data']['download_location']);
}

protected function downloadActionGifs(array $item): void
{

}
}
3 changes: 2 additions & 1 deletion src/Http/Resources/MediaResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public function toArray($request)
'url' => $this->getUrl(),
'thumb_url' => $this->isImageGif() ? $this->getUrl() : $this->getThumbUrl(),
'is_video' => $this->isVideo(),
'credit_url' => $this->credit_url ?? null
'credit_url' => $this->credit_url ?? null,
'download_data' => $this->download_data ?? null,
];
}
}
27 changes: 27 additions & 0 deletions src/Integrations/Unsplash/Jobs/TriggerDownloadJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Inovector\Mixpost\Integrations\Unsplash\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Inovector\Mixpost\Integrations\Unsplash\Unsplash;

class TriggerDownloadJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;

public function __construct(public readonly string $downloadLocation)
{
}

public function handle(Unsplash $unsplash): void
{
$unsplash->downloadPhoto($this->downloadLocation);
}
}
45 changes: 45 additions & 0 deletions src/Integrations/Unsplash/Unsplash.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Inovector\Mixpost\Integrations\Unsplash;

use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Http;
use Inovector\Mixpost\Facades\Services;
use Inovector\Mixpost\Util;

class Unsplash
{
protected string $clientId;
protected string $endpointUrl = 'https://api.unsplash.com';

public function __construct()
{
$clientId = Services::get('unsplash', 'client_id');

if (!$clientId) {
throw new \Exception('Unsplash is not configured.');
}

$this->clientId = $clientId;
}

public function photos(string $query = '', int $page = 1): array
{
return Http::get("$this->endpointUrl/search/photos", [
'client_id' => $this->clientId,
'query' => $query ?: Arr::random(Util::config('external_media_terms')),
'page' => $page,
'per_page' => 30,
])->json('results', []);
}

public function downloadPhoto(string $downloadLocation)
{
$download_path = parse_url($downloadLocation, PHP_URL_PATH);
$download_query = parse_url($downloadLocation, PHP_URL_QUERY);

return Http::get("$this->endpointUrl$download_path?$download_query", [
'client_id' => $this->clientId,
])->json();
}
}
Loading

0 comments on commit 9af04b4

Please sign in to comment.