diff --git a/app/Http/Controllers/TrackerController.php b/app/Http/Controllers/TrackerController.php index 3efc272..38d9552 100644 --- a/app/Http/Controllers/TrackerController.php +++ b/app/Http/Controllers/TrackerController.php @@ -219,6 +219,29 @@ public function createStory(CreateStoryTrackerRequest $request, InstagramScraper // Set story tracker $story['tracker_id'] = $tracker->id; + // Upload story thumbnail + if($request->hasFile('thumbnail')){ + $fileName = Str::slug($request->file('thumbnail')->getClientOriginalName()) . '_' . time() . '.' . $request->file('thumbnail')->getClientOriginalExtension(); + $thumbnailPath = $request->file('thumbnail')->storeAs('influencers/instagram/temp/stories/thumbnails/', $fileName, 'local'); + + if($thumbnailPath) + $story['thumbnail'] = $thumbnailPath; + } + + // Upload story video + if($request->hasFile('story')){ + $fileName = Str::slug($request->file('story')->getClientOriginalName()) . '_' . time() . '.' . $request->file('story')->getClientOriginalExtension(); + $videoPath = $request->file('thumbnail')->storeAs('influencers/instagram/temp/stories/videos/', $fileName, 'local'); + + if($videoPath) + $story['story'] = $videoPath; + } + + // Upload story proofs + if($request->hasFile('proofs')){ + + } + // Verify if influencer already exists $exists = Influencer::where([ 'platform' => 'instagram', @@ -237,35 +260,6 @@ public function createStory(CreateStoryTrackerRequest $request, InstagramScraper } return response()->success("Task executed in background, please wait...", [], 200); - // Create tracker - // $tracker = Tracker::create($request->validated()); - // $tracker = $tracker->refresh(); - - // // Upload story sequences - // $medias = []; - // if($request->hasFile('story')){ - // foreach($request->file('story') as $file){ - // $storyFileName = Str::slug($request->input('name') . '_') . time() . '.' . $file->getClientOriginalExtension(); - // $storyFilePath = $file->storeAs('uploads', $storyFileName, 'public'); - // // TODO: use story table insted of tracker media - // // $medias[] = TrackerMedia::create([ - // // 'tracker_id' => $tracker->id, - // // 'name' => $storyFileName, - // // 'type' => 'media', - // // 'media_path' => '/storage/' . $storyFilePath - // // ]); - // } - // } - - // // Srap Influencer - // $scraper = $scraper->authenticate(); - // $instagramUser = $scraper->byUsername($request->input('username')); - // $influencer = Influencer::create($instagramUser); - - // return response()->success( - // "Story tracker created successfully.", - // Tracker::with(['user', 'campaign', 'shortlink', 'infleuncers'])->find($tracker->id) - // ); } /** diff --git a/app/Http/Requests/CreateStoryTrackerRequest.php b/app/Http/Requests/CreateStoryTrackerRequest.php index 0176ae5..09a539c 100644 --- a/app/Http/Requests/CreateStoryTrackerRequest.php +++ b/app/Http/Requests/CreateStoryTrackerRequest.php @@ -31,7 +31,8 @@ public function rules() 'type' => 'required|in:story', 'platform' => 'nullable|in:instagram,snapchat', 'username' => 'required|string', - 'story' => 'required|max:50000|mimetypes:image/jpeg,image/png,image/gif,video/mp4,video/quicktime', + 'thumbnail' => 'required|max:50000|mimetypes:image/jpeg,image/png,image/gif', + 'story' => 'nullable|max:50000|mimetypes:video/mp4,video/quicktime', 'proofs.*' => 'nullable|max:50000|mimetypes:image/jpeg,image/png,image/gif', 'reach' => 'nullable|integer', 'impressions' => 'nullable|integer', diff --git a/app/Jobs/ScrapInfluencerJob.php b/app/Jobs/ScrapInfluencerJob.php index 352d5ad..2ccd022 100644 --- a/app/Jobs/ScrapInfluencerJob.php +++ b/app/Jobs/ScrapInfluencerJob.php @@ -4,17 +4,21 @@ use App\User; use App\Influencer; +use App\StoryAnalytics; use App\BrandInfluencer; use Illuminate\Bus\Queueable; +use Owenoj\LaravelGetId3\GetId3; +use Illuminate\Http\UploadedFile; +use InstagramScraper\Model\Story; use App\Services\InstagramScraper; use Illuminate\Support\Facades\Log; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Storage; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use App\Notifications\CreateInfluencerJobState; -use App\StoryAnalytics; -use InstagramScraper\Model\Story; +use Illuminate\Support\Facades\File; class ScrapInfluencerJob implements ShouldQueue { @@ -106,10 +110,34 @@ public function handle(InstagramScraper $instagram) // Handle story if(sizeof($this->story) > 0){ + // Copy thumbnail + if(Storage::disk('local')->exists($this->story['thumbnail'])){ + $oldPath = $this->story['thumbnail']; + $newPath = str_replace('temp', $influencer->id, $this->story['thumbnail']); + Storage::disk('local')->copy($this->story['thumbnail'], $newPath); + Storage::disk('local')->delete($oldPath); + $this->story['thumbnail'] = $newPath; + } + + // Copy video + if(Storage::disk('local')->exists($this->story['story'])){ + $oldPath = $this->story['story']; + $newPath = str_replace('temp', $influencer->id, $this->story['story']); + Storage::disk('local')->copy($this->story['story'], $newPath); + Storage::disk('local')->delete($oldPath); + $this->story['video'] = $newPath; + + // Get video duration + $video = new GetId3(new UploadedFile(Storage::disk('local')->path($newPath), File::name($newPath))); + $this->story['video_duration'] = $video->getPlaytimeSeconds(); + } + // Save story $story = Story::create([ 'influencer_id' => $influencer->id, 'tracker_id' => $this->story['tracker_id'], + 'thumbnail' => $this->story['thumbnail'], + 'video' => $this->story['video'], 'published_at' => $this->story['published_at'] ]); @@ -119,10 +147,11 @@ public function handle(InstagramScraper $instagram) 'reach' => $this->story['reach'], 'impressions' => $this->story['impressions'], 'interactions' => $this->story['interactions'], - 'back' => $this->story['back'], - 'forward' => $this->story['forward'], - 'next_story' => $this->story['next_story'], - 'exited' => $this->story['exited'], + 'back' => $this->story['back'] ?? 0, + 'forward' => $this->story['forward'] ?? 0, + 'next_story' => $this->story['next_story'] ?? 0, + 'exited' => $this->story['exited'] ?? 0, + 'navigation' => $this->story['back'] + $this->story['forward'] + $this->story['next_story'] + $this->story['exited'], ]); } } diff --git a/public/8.js b/public/8.js index c2f7b07..0ee9cd9 100644 --- a/public/8.js +++ b/public/8.js @@ -296,6 +296,11 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope // // // +// +// +// +// +// @@ -325,6 +330,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope loadCampaigns: function loadCampaigns() { this.$store.dispatch("fetchCampaigns")["catch"](function (e) {}); }, + handleThumbnailUpload: function handleThumbnailUpload(files) { + if (typeof files[0] === "undefined") return; + this.story = files[0]; + }, handleStoryUpload: function handleStoryUpload(files) { if (typeof files[0] === "undefined") return; this.story = files[0]; @@ -380,29 +389,32 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope formData.append("proofs[]", file); }); // Dispatch the creation action - this.$store.dispatch("addNewStory", formData).then(function (response) { - _this.createTrackerSuccess({ - message: "Story ".concat(response.content.name, " created successfuly!") - }); - - _this.$router.push({ - name: 'stories' - }); - })["catch"](function (error) { - var errors = Object.values(error.response.data.errors); + if (typeof this.$route.params.uuid === "undefined" || this.$route.params.uuid === null) { + this.$store.dispatch("addNewStory", formData).then(function (response) { + _this.createTrackerSuccess({ + message: "Story ".concat(response.content.name, " created successfuly!") + }); - if (_typeof(errors) === "object" && errors.length > 0) { - errors.forEach(function (element) { + _this.$router.push({ + name: 'trackers' + }); + })["catch"](function (error) { + var errors = Object.values(error.response.data.errors); + + if (_typeof(errors) === "object" && errors.length > 0) { + errors.forEach(function (element) { + _this.showError({ + message: element + }); + }); + } else { _this.showError({ - message: element + message: error.response.data.message }); - }); - } else { - _this.showError({ - message: error.response.data.message - }); - } - }); + } + }); + } else {// TODO: update exists story + } } }, mounted: function mounted() { @@ -858,24 +870,50 @@ var render = function() { "div", { staticClass: "control" }, [ - _c("label", [_vm._v("Story sequence")]), + _c("label", [_vm._v("Story screenshot or thumbnail")]), + _vm._v(" "), + _c("FileInput", { + attrs: { + id: "storyThumbnail", + label: "Upload story thumbnail", + accept: "image/jpeg,image/png,image/gif", + isList: true, + icon: "fas fa-plus", + multiple: false + }, + on: { custom: _vm.handleThumbnailUpload } + }), + _vm._v(" "), + _c("p", [ + _vm._v("A screenshot or thumbnail of story sequence.") + ]) + ], + 1 + ) + : _vm._e(), + _vm._v(" "), + !_vm.$route.params.uuid + ? _c( + "div", + { staticClass: "control" }, + [ + _c("label", [_vm._v("Story video")]), _vm._v(" "), _c("FileInput", { attrs: { - id: "storyfile", - label: "Upload story sequence", - accept: - "image/jpeg,image/png,image/gif,video/mp4,video/quicktime", + id: "storyVideo", + label: "Upload story video", + accept: "video/mp4,video/quicktime", isList: true, icon: "fas fa-plus", multiple: false }, - on: { custom: _vm.handleStoryUpload } + on: { custom: _vm.handleThumbnailUpload } }), _vm._v(" "), _c("p", [ _vm._v( - "If there are multiple images or videos for the story, we recommend creating one story per image or video." + "If story sequence is a video upload the video sequence." ) ]) ], @@ -891,7 +929,7 @@ var render = function() { _vm._v(" "), _c("FileInput", { attrs: { - id: "storyfile", + id: "storyProofs", label: "Upload story insights screenshots", accept: "image/jpeg,image/png,image/gif", isList: true, diff --git a/resources/js/pages/trackers/story.vue b/resources/js/pages/trackers/story.vue index f872a23..4155f9b 100644 --- a/resources/js/pages/trackers/story.vue +++ b/resources/js/pages/trackers/story.vue @@ -43,13 +43,18 @@
If there are multiple images or videos for the story, we recommend creating one story per image or video.
+ +A screenshot or thumbnail of story sequence.
+If story sequence is a video upload the video sequence.
If there are multiple images or videos for the story, we recommend creating one tracker per image or video.