Advertisement
roman_gemini

MyOwnRadio Tracklist Model

May 5th, 2015
251
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 11.73 KB | None | 0 0
  1. <?php
  2. /**
  3.  * Created by PhpStorm.
  4.  * UserModel: Roman
  5.  * Date: 15.12.14
  6.  * Time: 11:49
  7.  */
  8.  
  9. namespace Framework\Models;
  10.  
  11. use Framework\Exceptions\ControllerException;
  12. use Framework\Exceptions\UnauthorizedException;
  13. use Framework\Models\Traits\StreamControl;
  14. use Framework\Services\Database;
  15. use Framework\Services\DB\DBQuery;
  16. use Framework\Services\DB\DBQueryPool;
  17. use Framework\Services\DB\Query\DeleteQuery;
  18. use Framework\Services\DB\Query\SelectQuery;
  19. use Framework\Services\DB\Query\UpdateQuery;
  20. use Objects\Link;
  21. use Objects\StreamTrack;
  22. use Objects\Stream;
  23. use Objects\Track;
  24. use Tools\Common;
  25. use Tools\Optional;
  26. use Tools\Singleton;
  27. use Tools\SingletonInterface;
  28. use Tools\System;
  29.  
  30. /**
  31.  * Class PlaylistModel
  32.  * @package Model
  33.  */
  34. class PlaylistModel extends Model implements \Countable, SingletonInterface {
  35.  
  36.     use Singleton, StreamControl;
  37.  
  38.     protected $key;
  39.     /** @var UserModel $user */
  40.     private $user;
  41.     private $tracks_count;
  42.     private $tracks_duration;
  43.     private $status;
  44.     private $started_from;
  45.     private $started;
  46.  
  47.     public function __construct($id) {
  48.         parent::__construct();
  49.         $this->user = AuthUserModel::getInstance();
  50.         $this->key = $id;
  51.         $this->reload();
  52.     }
  53.  
  54.     /**
  55.      * @throws ControllerException
  56.      * @return $this
  57.      */
  58.     public function reload() {
  59.  
  60.         Database::doInConnection(function (Database $db) {
  61.  
  62.             $query = $db->getDBQuery()->selectFrom("r_streams a")
  63.                 ->innerJoin("r_static_stream_vars b", "a.sid = b.stream_id")
  64.                 ->where("a.sid", $this->key)
  65.                 ->select("a.uid, a.started, a.started_from, a.status, b.tracks_count, b.tracks_duration");
  66.  
  67.             $stats = $db->fetchOneRow($query)
  68.                 ->getOrElseThrow(ControllerException::noStream($this->key));
  69.  
  70.             if (intval($stats["uid"]) !== $this->user->getID()) {
  71.                 throw UnauthorizedException::noPermission();
  72.             }
  73.  
  74.             $this->tracks_count = intval($stats["tracks_count"]);
  75.             $this->tracks_duration = intval($stats["tracks_duration"]);
  76.  
  77.             $this->status = intval($stats["status"]);
  78.             $this->started = intval($stats["started"]);
  79.             $this->started_from = intval($stats["started_from"]);
  80.  
  81.         });
  82.  
  83.         return $this;
  84.  
  85.     }
  86.  
  87.     /**
  88.      * @return mixed
  89.      */
  90.     public function getTrackInStream() {
  91.         return $this->tracks_count;
  92.     }
  93.  
  94.     /**
  95.      * @param $tracks
  96.      * @param bool $upNext
  97.      * @return $this
  98.      */
  99.     public function addTracks($tracks, $upNext = false) {
  100.  
  101.         $this->doAtomic(function () use (&$tracks, &$upNext) {
  102.  
  103.             $tracksToAdd = explode(",", $tracks);
  104.             $initialPosition = $this->tracks_count;
  105.             $initialTimeOffset = $this->tracks_duration;
  106.  
  107.             if ($upNext) {
  108.                 $nowPlaying = $this->getPlayingTrack();
  109.             } else {
  110.                 $nowPlaying = Optional::noValue();
  111.             }
  112.  
  113.             foreach ($tracksToAdd as $track) {
  114.  
  115.                 Track::getByID($track)
  116.                     ->then(function ($trackObject) use (&$initialPosition, &$initialTimeOffset, &$nowPlaying) {
  117.  
  118.                         /** @var Track $trackObject */
  119.  
  120.                         // Skip foreign tracks
  121.                         if ($trackObject->getUserID() != $this->user->getID()) return;
  122.  
  123.                         $uniqueID = $this->generateUniqueID();
  124.  
  125.                         $linker = new Link();
  126.  
  127.                         $linker->setStreamID($this->key);
  128.                         $linker->setTrackID($trackObject->getID());
  129.                         $linker->setTrackOrder(++$initialPosition);
  130.                         $linker->setUniqueID($uniqueID);
  131.                         $linker->setTimeOffset($initialTimeOffset);
  132.  
  133.                         $linker->save();
  134.  
  135.                         $nowPlaying->then(function (StreamTrack $track) use (&$uniqueID) {
  136.  
  137.                             logger(sprintf("Now playing track with index = %d", $track->getTrackOrder()));
  138.  
  139.                             Database::doInConnection(function (Database $db) use (&$uniqueID, &$track) {
  140.                                 $db->executeUpdate("SELECT move_track_channel(?, ?, ?)", [
  141.                                     $this->key, $uniqueID, $track->getTrackOrder() + 1
  142.                                 ]);
  143.                             });
  144.  
  145.                         });
  146.  
  147.                         $initialTimeOffset += $trackObject->getDuration();
  148.  
  149.                     });
  150.  
  151.             }
  152.  
  153.             if ($this->tracks_count == 0) {
  154.                 PlaylistModel::getInstance($this->key)->scPlay();
  155.             }
  156.  
  157.         });
  158.  
  159.         return $this;
  160.  
  161.     }
  162.  
  163.     /**
  164.      * @param callable $callable
  165.      * @return $this
  166.      */
  167.     private function doAtomic(callable $callable) {
  168.  
  169.         $this->getPlayingTrack()->then(function ($track) use ($callable) {
  170.  
  171.             /** @var StreamTrack $track */
  172.             $position = $this->getStreamPosition()->get();
  173.             $trackPosition = $position - $track->getTimeOffset();
  174.  
  175.             logger("Now playing: " . $track->getFileName());
  176.             logger("Offset: " . number_format($trackPosition / 1000));
  177.  
  178.             call_user_func($callable);
  179.  
  180.             if (StreamTrack::getByID($track->getUniqueID())->validate()) {
  181.                 $this->scPlayByUniqueID($track->getUniqueID(), $trackPosition, false);
  182.             } else {
  183.                 $this->scPlayByOrderID($track->getTrackOrder());
  184.             }
  185.  
  186.         })->getOrElseCallback($callable);
  187.  
  188.         return $this;
  189.  
  190.     }
  191.  
  192.     /**
  193.      * @param int|null $time
  194.      * @return Optional
  195.      */
  196.     public function getPlayingTrack($time = null) {
  197.  
  198.         $position = $this->getStreamPosition($time)->getOrElseNull();
  199.  
  200.         if (is_null($position)) {
  201.             return Optional::noValue();
  202.         }
  203.  
  204.         return $this->getTrackByTime($position);
  205.  
  206.     }
  207.  
  208.     /**
  209.      * @param int|null $time
  210.      * @return Optional
  211.      */
  212.     public function getStreamPosition($time = null) {
  213.  
  214.         if ($this->tracks_duration == 0) {
  215.             return Optional::ofNullable(0);
  216.         }
  217.  
  218.         if ($this->status == 0) {
  219.             return Optional::ofNullable(null);
  220.         }
  221.  
  222.         if (is_null($time)) {
  223.             $time = System::time();
  224.         }
  225.  
  226.         $position = ($time - $this->started + $this->started_from) % $this->tracks_duration;
  227.  
  228.         return Optional::ofNullable($position);
  229.  
  230.     }
  231.  
  232.     /**
  233.      * @param $time
  234.      * @return Optional
  235.      */
  236.     public function getTrackByTime($time) {
  237.  
  238.         if ($this->getStreamDuration() == 0) {
  239.             return Optional::noValue();
  240.         }
  241.  
  242.         $mod = $time % $this->getStreamDuration();
  243.  
  244.         return $this->_getPlaylistTrack(
  245.             "b.time_offset <= :time AND b.time_offset + a.duration >= :time AND b.stream_id = :id",
  246.             [":time" => $mod, ":id" => $this->key]
  247.         );
  248.  
  249.     }
  250.  
  251.     /**
  252.      * @return mixed
  253.      */
  254.     public function getStreamDuration() {
  255.         return $this->tracks_duration;
  256.     }
  257.  
  258.     /**
  259.      * @param string $filter
  260.      * @param array $args
  261.      * @return Optional
  262.      */
  263.     protected function _getPlaylistTrack($filter, array $args = null) {
  264.  
  265.         return Database::doInConnection(function (Database $db) use ($filter, $args) {
  266.  
  267.             $query = $this->getTrackQueryPrefix();
  268.             $query->limit(1);
  269.             $query->where($filter, $args);
  270.  
  271.             return $db->fetchOneObject($query, null, "Objects\\StreamTrack");
  272.  
  273.         });
  274.  
  275.     }
  276.  
  277.     /**
  278.      * @return \Framework\Services\DB\Query\SelectQuery
  279.      */
  280.     private function getTrackQueryPrefix() {
  281.  
  282.         $query = DBQuery::getInstance()->selectFrom("r_tracks a")
  283.             ->innerJoin("r_link b", "a.tid = b.track_id");
  284.  
  285.         $query->select("a.*, b.unique_id, b.t_order, b.time_offset");
  286.  
  287.         return $query;
  288.  
  289.     }
  290.  
  291.     public function generateUniqueID() {
  292.  
  293.         return Database::doInConnection(function (Database $conn) {
  294.  
  295.             do {
  296.                 $generated = Common::generateUniqueID();
  297.             } while ($conn->fetchOneColumn("SELECT COUNT(*) FROM r_link WHERE unique_id = ?", [$generated])->get());
  298.  
  299.             return $generated;
  300.  
  301.         });
  302.  
  303.  
  304.     }
  305.  
  306.     /**
  307.      * @param $tracks
  308.      * @return $this
  309.      */
  310.     public function removeTracks($tracks) {
  311.  
  312.         $this->doAtomic(function () use (&$tracks) {
  313.  
  314.             (new DeleteQuery("r_link"))
  315.                 ->where("FIND_IN_SET(unique_id, ?)", [$tracks])
  316.                 ->where("stream_id", $this->key)
  317.                 ->update();
  318.  
  319.             $this->optimize();
  320.  
  321.         });
  322.  
  323.         return $this;
  324.  
  325.     }
  326.  
  327.     /**
  328.      * @return $this
  329.      */
  330.     public function shuffleTracks() {
  331.  
  332.         $this->doAtomic(function () {
  333.  
  334.             Database::doInConnection(function (Database $db) {
  335.                 $db->executeUpdate("CALL shuffle_channel(?)", [$this->key]);
  336.             });
  337.  
  338.         });
  339.  
  340.         return $this;
  341.  
  342.     }
  343.  
  344.     public function optimize() {
  345.  
  346.         $this->doAtomic(function () {
  347.  
  348.             Database::doInConnection(function (Database $db) {
  349.                 $db->executeUpdate("CALL optimize_channel(?)", [$this->key]);
  350.             });
  351.  
  352.         });
  353.  
  354.     }
  355.  
  356.     /**
  357.      * @param $uniqueID
  358.      * @param $index
  359.      * @return $this
  360.      */
  361.     public function moveTrack($uniqueID, $index) {
  362.  
  363.         $this->doAtomic(function () use (&$uniqueID, &$index) {
  364.  
  365.             Database::doInConnection(function (Database $db) use (&$uniqueID, &$index) {
  366.                 $db->executeUpdate("CALL move_track_channel(?, ?, ?)", [$this->key, $uniqueID, $index]);
  367.             });
  368.  
  369.         });
  370.  
  371.         return $this;
  372.  
  373.     }
  374.  
  375.     /**
  376.      * @param $id
  377.      * @return Optional
  378.      */
  379.     public function getTrackByOrder($id) {
  380.  
  381.         return $this->_getPlaylistTrack("b.t_order = ? AND b.stream_id = ?", [$id, $this->key]);
  382.  
  383.     }
  384.  
  385.     /**
  386.      * @return int
  387.      */
  388.     public function count() {
  389.         return intval($this->tracks_count);
  390.     }
  391.  
  392.     /**
  393.      * @param StreamTrack $trackBean
  394.      * @param int $startFrom
  395.      * @param bool $notify
  396.      */
  397.     protected function _setCurrentTrack(StreamTrack $trackBean, $startFrom = 0, $notify = true) {
  398.  
  399.         Stream::getByID($this->key)
  400.             ->then(function ($stream) use ($trackBean, $startFrom) {
  401.  
  402.                 logger("Restored: " . $trackBean->getFileName());
  403.                 logger("Offset: " . number_format($startFrom / 1000));
  404.  
  405.                 /** @var Stream $stream */
  406.                 $stream->setStartedFrom($trackBean->getTimeOffset() + $startFrom);
  407.                 $stream->setStarted(System::time());
  408.                 $stream->setStatus(1);
  409.                 $stream->save();
  410.             });
  411.  
  412.  
  413.         if ($notify == true) {
  414.             $this->notifyStreamers();
  415.         }
  416.  
  417.     }
  418.  
  419.     public function notifyStreamers() {
  420.         self::notifyAllStreamers($this->key);
  421.     }
  422.  
  423.     public static function notifyAllStreamers($streamID) {
  424.  
  425.         $ch = curl_init("http://127.0.0.1:7778/notify?s=" . $streamID);
  426.  
  427.         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  428.         curl_setopt($ch, CURLOPT_VERBOSE, true);
  429.         curl_setopt($ch, CURLOPT_TIMEOUT, 5);
  430.         curl_exec($ch);
  431.         curl_close($ch);
  432.  
  433.     }
  434.  
  435.     /**
  436.      * @return Optional
  437.      */
  438.     protected function _getRandomTrack() {
  439.  
  440.         $query = $this->getTrackQueryPrefix();
  441.         $query->orderBy("RAND()");
  442.         $query->limit(1);
  443.         $query->where("b.stream_id", $this->key);
  444.  
  445.         return $query->fetchObject($query, null, StreamTrack::className());
  446.  
  447.     }
  448.  
  449. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement