~xibo-maintainers/xibo/tempel

« back to all changes in this revision

Viewing changes to lib/Entity/Media.php

  • Committer: GitHub
  • Author(s): Dan Garner
  • Date: 2016-11-16 11:51:16 UTC
  • Revision ID: git-v1:1cdd39222ff9fdd3fcc0bb8924cc706a24b18905
Bugfix/1.8.0 pack1 (#227)

* Library Tidy inaccessible
* Enabled notifications tidy task by default
* Implementation of "parent settings" so that TwitterMetro can use the API key/secret from Twitter.
* Fix for StatsArchive task infinite loop
* Fix for StatsArchive task processing ahead of now
* Flip clock to support countdown from a particular date, and daily countdown. xibosignage/xibo#936
* Fix date parsing when a timezone has been set
* Only save campaign if changes were made.
* Fix display notifications for removing dups
* XMR priv socket sleep to 100 ms
* Fix removal of Twig sources on release archive build. xibosignage/xibo#940
* Improve campaign layout assign/unassign xibosignage/xibo#941
* Tidy up unnecessary save in MediaInventory. Better logging for errors with sendfile from XMDS.
* Fix display status icon xibosignage/xibo#943
* Updated caching logic for required files
* Rewrite the nonce cache (required file) so that it uses a key per file instead of one storage object.
* Rework required files again - move back into the DB, but optimize nonce creation to ease contention.
* Further improvements to DB activity, basic connection management and stats reporting. Also deadlock protection statement.
* XMDS not updating bytes requested.
* More logging for retry deadlock loop
* Correct bandwidth logging. Fix deadlock loop.
* Improvements to media download (proper async). Fix for module downloads being copied rather than moved.
* Swagger doc fixes
* Convert XTR into a select and run single process script. Should fix it for windows.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
namespace Xibo\Entity;
24
24
 
25
25
 
26
 
use GuzzleHttp\Client;
27
 
use GuzzleHttp\Exception\RequestException;
28
 
use GuzzleHttp\Promise\Promise;
29
 
use GuzzleHttp\Promise\PromiseInterface;
30
26
use Respect\Validation\Validator as v;
31
27
use Xibo\Exception\ConfigurationException;
32
28
use Xibo\Exception\DuplicateEntityException;
179
175
    private $unassignTags = [];
180
176
 
181
177
    // New file revision
182
 
    public $force;
 
178
    public $isSaveRequired;
183
179
    public $isRemote;
 
180
 
184
181
    public $cloned = false;
185
182
    public $newExpiry;
186
183
    public $alwaysCopy = false;
475
472
    /**
476
473
     * Save this media
477
474
     * @param array $options
478
 
     * @return null|PromiseInterface
479
475
     */
480
476
    public function save($options = [])
481
477
    {
 
478
        $this->getLog()->debug('Save for mediaId: ' . $this->mediaId);
 
479
 
482
480
        $options = array_merge([
483
481
            'validate' => true,
484
482
            'oldMedia' => null,
490
488
 
491
489
        // Add or edit
492
490
        if ($this->mediaId == null || $this->mediaId == 0) {
493
 
            $promise = $this->add();
 
491
            $this->add();
 
492
 
 
493
            // Always set force to true as we always want to save new files
 
494
            $this->isSaveRequired = true;
494
495
        }
495
496
        else {
 
497
            $this->edit();
 
498
 
496
499
            // If the media file is invalid, then force an update (only applies to module files)
497
 
            if ($this->valid == 0)
498
 
                $this->force = true;
499
 
 
500
 
            $promise = $this->edit();
 
500
            $expires = $this->getOriginalValue('expires');
 
501
            $this->isSaveRequired = ($this->valid == 0 || ($expires > 0 && $expires < time()));
501
502
        }
502
503
 
503
 
        if (!$options['deferred']) {
504
 
            $this->getLog()->debug('Handling promise immediately.');
505
 
            $promise->wait();
 
504
        if ($options['deferred']) {
 
505
            $this->getLog()->debug('Media Update deferred until later');
506
506
        } else {
507
 
            $this->getLog()->debug('Async Upload, returning promise');
 
507
            $this->getLog()->debug('Media Update happening now');
 
508
 
 
509
            // Call save file
 
510
            if ($this->isSaveRequired)
 
511
                $this->saveFile();
508
512
        }
509
513
 
510
514
        // Save the tags
524
528
                $tag->save();
525
529
            }
526
530
        }
527
 
 
528
 
        return $options['deferred'] ? $promise : null;
529
531
    }
530
532
 
531
533
    /**
532
534
     * Save Async
533
535
     * @param array $options
534
 
     * @return PromiseInterface
 
536
     * @return $this
535
537
     */
536
538
    public function saveAsync($options = [])
537
539
    {
539
541
            'deferred' => true
540
542
        ], $options);
541
543
 
542
 
        return $this->save($options);
 
544
        $this->save($options);
 
545
 
 
546
        return $this;
543
547
    }
544
548
 
545
549
    /**
638
642
 
639
643
    /**
640
644
     * Add
641
 
     * @return PromiseInterface
642
645
     * @throws ConfigurationException
643
646
     */
644
647
    private function add()
645
648
    {
646
649
        $this->mediaId = $this->getStore()->insert('
647
 
            INSERT INTO media (name, type, duration, originalFilename, userID, retired, moduleSystemFile, expires, released, apiRef)
648
 
              VALUES (:name, :type, :duration, :originalFileName, :userId, :retired, :moduleSystemFile, :expires, :released, :apiRef)
 
650
            INSERT INTO `media` (`name`, `type`, duration, originalFilename, userID, retired, moduleSystemFile, released, apiRef, valid)
 
651
              VALUES (:name, :type, :duration, :originalFileName, :userId, :retired, :moduleSystemFile, :released, :apiRef, :valid)
649
652
        ', [
650
653
            'name' => $this->name,
651
654
            'type' => $this->mediaType,
654
657
            'userId' => $this->ownerId,
655
658
            'retired' => $this->retired,
656
659
            'moduleSystemFile' => (($this->moduleSystemFile) ? 1 : 0),
657
 
            'expires' => $this->expires,
658
660
            'released' => $this->released,
659
 
            'apiRef' => $this->apiRef
 
661
            'apiRef' => $this->apiRef,
 
662
            'valid' => 0
660
663
        ]);
661
 
 
662
 
        // Store $this
663
 
        $media = $this;
664
 
 
665
 
        // Create a promise
666
 
        $promise = new Promise(function() use (&$promise) {
667
 
            $promise->resolve('Waited');
668
 
        });
669
 
        $promise->then(function ($value) use (&$media) {
670
 
            $media->getLog()->debug('Media save promise resolved ' . $value);
671
 
 
672
 
            $media->saveFile();
673
 
 
674
 
            // Update the MD5 and storedAs to suit
675
 
            $media->getStore()->update('UPDATE `media` SET md5 = :md5, fileSize = :fileSize, storedAs = :storedAs, duration = :duration WHERE mediaId = :mediaId', [
676
 
                'fileSize' => $this->fileSize,
677
 
                'md5' => $this->md5,
678
 
                'storedAs' => $this->storedAs,
679
 
                'duration' => $this->duration,
680
 
                'mediaId' => $this->mediaId
681
 
            ]);
682
 
 
683
 
        }, function ($reason) use (&$media)  {
684
 
            // Promise rejected - that's bad.
685
 
            $media->getLog()->error('Unable to download media because ' . $reason);
686
 
        });
687
 
 
688
 
        return $promise;
689
664
    }
690
665
 
691
666
    /**
692
667
     * Edit
693
 
     * @return PromiseInterface
694
668
     * @throws ConfigurationException
695
669
     */
696
670
    private function edit()
697
671
    {
698
 
        // Store $this
699
 
        $media = $this;
700
 
 
701
 
        // Create a promise
702
 
        $promise = new Promise(function() use (&$promise) {
703
 
            $promise->resolve('Waited');
704
 
        });
705
 
        $promise->then(function ($value) use (&$media) {
706
 
            $media->getLog()->debug('Media save promise resolved ' . $value);
707
 
 
708
 
            // Do we need to pull a new update?
709
 
            // Is the file either expired or is force set
710
 
            if ($media->force || ($media->expires > 0 && $media->expires < time())) {
711
 
                $media->getLog()->debug('Media %s has expired: %s. Force = %d', $media->name, date('Y-m-d H:i', $media->expires), $media->force);
712
 
 
713
 
                $media->saveFile();
714
 
            }
715
 
 
716
 
            $media->getStore()->update('
717
 
              UPDATE `media`
718
 
                  SET `name` = :name,
719
 
                    duration = :duration,
720
 
                    retired = :retired,
721
 
                    md5 = :md5,
722
 
                    filesize = :fileSize,
723
 
                    expires = :expires,
724
 
                    moduleSystemFile = :moduleSystemFile,
725
 
                    editedMediaId = :editedMediaId,
726
 
                    isEdited = :isEdited,
727
 
                    userId = :userId,
728
 
                    released = :released,
729
 
                    apiRef = :apiRef
730
 
               WHERE mediaId = :mediaId
731
 
            ', [
732
 
                'name' => $this->name,
733
 
                'duration' => $this->duration,
734
 
                'retired' => $this->retired,
735
 
                'fileSize' => $this->fileSize,
736
 
                'md5' => $this->md5,
737
 
                'expires' => $this->expires,
738
 
                'moduleSystemFile' => $this->moduleSystemFile,
739
 
                'editedMediaId' => $this->parentId,
740
 
                'isEdited' => $this->isEdited,
741
 
                'userId' => $this->ownerId,
742
 
                'released' => $this->released,
743
 
                'apiRef' => $this->apiRef,
744
 
                'mediaId' => $this->mediaId
745
 
            ]);
746
 
 
747
 
        }, function ($reason) use (&$media)  {
748
 
            // Promise rejected - that's bad.
749
 
            $media->getLog()->error('Unable to download media because ' . $reason);
750
 
        });
751
 
 
752
 
        return $promise;
 
672
        $this->getStore()->update('
 
673
          UPDATE `media`
 
674
            SET `name` = :name,
 
675
                duration = :duration,
 
676
                retired = :retired,
 
677
                moduleSystemFile = :moduleSystemFile,
 
678
                editedMediaId = :editedMediaId,
 
679
                isEdited = :isEdited,
 
680
                userId = :userId,
 
681
                released = :released,
 
682
                apiRef = :apiRef
 
683
           WHERE mediaId = :mediaId
 
684
        ', [
 
685
            'name' => $this->name,
 
686
            'duration' => $this->duration,
 
687
            'retired' => $this->retired,
 
688
            'moduleSystemFile' => $this->moduleSystemFile,
 
689
            'editedMediaId' => $this->parentId,
 
690
            'isEdited' => $this->isEdited,
 
691
            'userId' => $this->ownerId,
 
692
            'released' => $this->released,
 
693
            'apiRef' => $this->apiRef,
 
694
            'mediaId' => $this->mediaId
 
695
        ]);
753
696
    }
754
697
 
755
698
    /**
756
699
     * Save File to Library
757
 
     *  this should download remote files, handle clones, handle local module files and also handle files uploaded
758
 
     *  over the web ui
 
700
     *  works on files that are already in the File system
759
701
     * @throws ConfigurationException
760
702
     */
761
703
    public function saveFile()
762
704
    {
763
 
        // If we are a remote media item, we want to download the newFile and save it to a temporary location
764
 
        if ($this->isRemote) {
765
 
            $this->download();
766
 
        }
767
 
 
768
705
        $libraryFolder = $this->config->GetSetting('LIBRARY_LOCATION');
769
706
 
770
707
        // Work out the extension
771
708
        $extension = strtolower(substr(strrchr($this->fileName, '.'), 1));
772
709
 
773
 
        $this->getLog()->debug('saveFile for "%s" with storedAs = "%s" (empty = %s), fileName = "%s" to "%s". Always Copy = "%s", Cloned = "%s"',
 
710
        $this->getLog()->debug('saveFile for "%s" with storedAs = "%s", fileName = "%s" to "%s". Always Copy = "%s", Cloned = "%s"',
774
711
            $this->name,
775
712
            $this->storedAs,
776
 
            empty($this->storedAs),
777
713
            $this->fileName,
778
714
            $this->mediaId . '.' . $extension,
779
715
            $this->alwaysCopy,
801
737
            $this->storedAs = $this->mediaId . '.' . $extension;
802
738
        }
803
739
        else {
804
 
            $this->getLog()->debug('Copying specified file');
805
740
            // We have pre-defined where we want this to be stored
806
741
            if (empty($this->storedAs)) {
807
742
                // Assume we want to set this automatically (i.e. we are set to always copy)
808
743
                $this->storedAs = $this->mediaId . '.' . $extension;
809
744
            }
810
745
 
811
 
            if (!@copy($this->fileName, $libraryFolder . $this->storedAs)) {
812
 
                $this->getLog()->error('Cannot copy %s to %s', $this->fileName, $libraryFolder . $this->storedAs);
813
 
                throw new ConfigurationException(__('Problem moving provided file into the Library Folder'));
 
746
            if ($this->isRemote) {
 
747
                $this->getLog()->debug('Moving temporary file');
 
748
 
 
749
                // Move the file into the library
 
750
                if (!@rename($libraryFolder . 'temp/' . $this->fileName, $libraryFolder . $this->storedAs))
 
751
                    throw new ConfigurationException(__('Problem moving downloaded file into the Library Folder'));
 
752
            } else {
 
753
                $this->getLog()->debug('Copying specified file');
 
754
 
 
755
                if (!@copy($this->fileName, $libraryFolder . $this->storedAs)) {
 
756
                    $this->getLog()->error('Cannot copy %s to %s', $this->fileName, $libraryFolder . $this->storedAs);
 
757
                    throw new ConfigurationException(__('Problem copying provided file into the Library Folder'));
 
758
                }
814
759
            }
815
760
        }
816
761
 
817
762
        // Work out the MD5
818
763
        $this->md5 = md5_file($libraryFolder . $this->storedAs);
819
764
        $this->fileSize = filesize($libraryFolder . $this->storedAs);
 
765
 
 
766
        // Update the MD5 and storedAs to suit
 
767
        $this->getStore()->update('UPDATE `media` SET md5 = :md5, fileSize = :fileSize, storedAs = :storedAs, expires = :expires, valid = 1 WHERE mediaId = :mediaId', [
 
768
            'fileSize' => $this->fileSize,
 
769
            'md5' => $this->md5,
 
770
            'storedAs' => $this->storedAs,
 
771
            'expires' => $this->expires,
 
772
            'mediaId' => $this->mediaId
 
773
        ]);
820
774
    }
821
775
 
822
776
    /**
855
809
    }
856
810
 
857
811
    /**
858
 
     * Download remote file
859
 
     */
860
 
    private function download()
861
 
    {
862
 
        if (!$this->isRemote || $this->fileName == '')
863
 
            throw new \InvalidArgumentException(__('Not in a suitable state to download'));
864
 
 
865
 
        // Open the temporary file
866
 
        $storedAs = $this->config->GetSetting('LIBRARY_LOCATION') . 'temp' . DIRECTORY_SEPARATOR . $this->name;
867
 
 
868
 
        $this->getLog()->debug('Downloading %s to %s', $this->fileName, $storedAs);
869
 
 
870
 
        if (!$fileHandle = fopen($storedAs, 'w'))
871
 
            throw new ConfigurationException(__('Temporary location not writable'));
872
 
 
873
 
        try {
874
 
            $client = new Client();
875
 
            $client->get($this->fileName, $this->config->getGuzzleProxy(['save_to' => $fileHandle]));
876
 
        }
877
 
        catch (RequestException $e) {
878
 
            $this->getLog()->error('Unable to get %s, %s', $this->fileName, $e->getMessage());
879
 
        }
880
 
 
881
 
        // Change the filename to our temporary file
882
 
        $this->fileName = $storedAs;
 
812
     * Download URL
 
813
     * @return string
 
814
     */
 
815
    public function downloadUrl()
 
816
    {
 
817
        return $this->fileName;
 
818
    }
 
819
 
 
820
    /**
 
821
     * Download Sink
 
822
     * @return string
 
823
     */
 
824
    public function downloadSink()
 
825
    {
 
826
        return $this->config->GetSetting('LIBRARY_LOCATION') . 'temp' . DIRECTORY_SEPARATOR . $this->name;
883
827
    }
884
828
}
 
 
b'\\ No newline at end of file'