Issues (4)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/AudioPlayer.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * @author Navarr Barnier <[email protected]>
5
 * @license MIT
6
 */
7
namespace Navarr\YouTube;
8
9
class AudioPlayer
10
{
11
    const SIZE_INVISIBLE = 0;
12
    const SIZE_TINY = 1;
13
    const SIZE_SMALL = 2;
14
    const SIZE_MEDIUM = 3;
15
    const SIZE_LARGE = 4;
16
17
    const THEME_LIGHT = 'light';
18
    const THEME_DARK = 'dark';
19
20
    const TYPE_VIDEO = 1;
21
    const TYPE_PLAYLIST = 2;
22
23
    protected $id = null;
24
    protected $https = true;
25
    protected $type = self::TYPE_VIDEO;
26
    protected $size = self::SIZE_SMALL;
27
    protected $source = null;
28
    protected $hd = false;
29
    protected $autoplay = false;
30
    protected $jsapi = false;
31
    protected $progressbar = false;
32
    protected $timecode = false;
33
    protected $cookies = true;
34
    protected $theme = self::THEME_DARK;
35
    protected $loop = false;
36
37
    /**
38
     * Constructor.
39
     *
40
     *
41
     * @param string $source
42
     * @param array  $settings
43
     *
44
     * @throws AudioPlayerException
45
     */
46 12
    public function __construct($source, $settings = null)
47
    {
48 12
        $this->source($source);
49
50 12
        if ($settings === null) {
51 12
            $settings = [];
52 12
        }
53
54
        // Feature array, ie: array('https','hd','autoplay')
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
55 12
        if (in_array('https', $settings)) {
56
            $this->https();
57
        }
58 12
        if (in_array('hd', $settings)) {
59
            $this->hd();
60
        }
61 12
        if (in_array('autoplay', $settings)) {
62
            $this->autoplay();
63
        }
64 12
        if (in_array('jsapi', $settings)) {
65
            $this->jsAPI();
66
        }
67 12
        if (in_array('progressbar', $settings)) {
68
            $this->progressBar();
69
        }
70 12
        if (in_array('timecode', $settings)) {
71
            $this->timeCode();
72
        }
73 12
        if (in_array('cookies', $settings)) {
74
            $this->cookies();
75
        }
76 12
        if (in_array('loop', $settings)) {
77
            $this->loop();
78
        }
79
80
        // Associative Feature Array, ie: array('https' => true, 'hd' => false)
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
81 12
        if (isset($settings['https'])) {
82
            $this->https($settings['https']);
83
        }
84 12
        if (isset($settings['size'])) {
85
            $this->size($settings['size']);
86
        }
87 12
        if (isset($settings['hd'])) {
88
            $this->hd($settings['hd']);
89
        }
90 12
        if (isset($settings['autoplay'])) {
91
            $this->autoplay($settings['autoplay']);
92
        }
93 12
        if (isset($settings['jsapi'])) {
94
            $this->jsAPI($settings['jsapi']);
95
        }
96 12
        if (isset($settings['progressbar'])) {
97
            $this->progressBar($settings['progressbar']);
98
        }
99 12
        if (isset($settings['timecode'])) {
100
            $this->timeCode($settings['timecode']);
101
        }
102 12
        if (isset($settings['cookies'])) {
103
            $this->cookies($settings['cookies']);
104
        }
105 12
        if (isset($settings['theme'])) {
106
            $this->theme($settings['theme']);
107
        }
108 12
        if (isset($settings['loop'])) {
109
            $this->loop($settings['loop']);
110
        }
111 12
    }
112
113
    /**
114
     * Factory
115
     * Allows easy creation and daisy-chaining of a YTAudio object.
116
     *
117
     * @see __construct
118
     *
119
     * @param string $source
120
     * @param array  $settings
121
     *
122
     * @throws AudioPlayerException
123
     *
124
     * @return static
125
     */
126
    public static function create($source, $settings = null)
127
    {
128
        return new static($source, $settings);
129
    }
130
131
    /**
132
     * Set Player Video/Playlist
133
     * Does not validate whether or not YouTube video/playlist exists.
134
     *
135
     *
136
     * @param string $source Can be a URL or just the ID
137
     *
138
     * @throws AudioPlayerException
139
     *
140
     * @return $this
141
     */
142 12
    public function source($source)
143
    {
144 12
        $oldSource = $this->source;
145
146 12
        $this->source = $source;
147 12
        $parsedURL = parse_url($source);
148
149
        // 1 thing, auto-detect based on ID
150 12
        if (count($parsedURL) === 1) {
151
            if (substr(strtoupper($parsedURL['path']), 0, 2) == 'PL') {
152
                return $this->playlist($source);
153
            } else {
154
                return $this->video($source);
155
            }
156
        }
157
158
        // Else, try to detect in turn.
159
        try {
160 12
            return $this->video($source);
161
        } catch (AudioPlayerException $e) {
162
        } // do nothing.  Might be playlist.
163
164
        try {
165
            return $this->playlist($source);
166
        } catch (AudioPlayerException $e) {
167
            $this->source = $oldSource;
168
            throw new AudioPlayerException('Could not detect source');
169
        }
170
    }
171
172
    /**
173
     * Set Player Video
174
     * Does not validate whether or not YouTube video exists.
175
     *
176
     *
177
     * @param string $video Can be a URL or just the ID
178
     *
179
     * @throws AudioPlayerException
180
     *
181
     * @return $this
182
     */
183 12
    public function video($video)
184
    {
185 12
        $oldSource = $this->source;
186 12
        $this->source = $video;
187
188 12
        $parsedURL = parse_url($video);
189
190
        // 1 thing, assume video.
191 12
        if (count($parsedURL) === 1) {
192
            $this->type = static::TYPE_VIDEO;
193
            $this->id = $parsedURL['path'];
194
195
            return $this;
196
        }
197
198
        // Youtu.be - Video
199 12
        if (strtolower($parsedURL['host']) == 'youtu.be') {
200
            $this->type = static::TYPE_VIDEO;
201
            $this->id = substr($parsedURL['path'], 1);
202
203
            return $this;
204
        }
205
206
        // Assume its a YouTube URL
207
        // check for /watch
208 12
        if (strtolower($parsedURL['path']) == '/watch') {
209 12
            $parsedQuery = explode('&', $parsedURL['query']);
210
            // Find v=
211 12 View Code Duplication
            foreach ($parsedQuery as $v) {
212 12
                if (substr(strtolower($v), 0, 2) == 'v=') {
213 12
                    $this->type = static::TYPE_VIDEO;
214 12
                    $this->id = substr($v, 2);
215
216 12
                    return $this;
217
                }
218
            }
219
        }
220
221
        // check for /v/
222
        if (substr(strtolower($parsedURL['path']), 0, 3) == '/v/') {
223
            $this->type = static::TYPE_VIDEO;
224
            $this->id = substr($parsedURL['path'], 0, 3);
225
226
            return $this;
227
        }
228
229
        $this->source = $oldSource;
230
        throw new AudioPlayerException('Could not identify video');
231
    }
232
233
    /**
234
     * Set Player Playlist
235
     * Does not validate whether or not YouTube playlist exists.
236
     *
237
     *
238
     * @param string $playlist Can be a URL or just the ID
239
     *
240
     * @throws AudioPlayerException
241
     *
242
     * @return $this
243
     */
244
    public function playlist($playlist)
245
    {
246
        $oldSource = $this->source;
247
        $this->source = $playlist;
248
249
        $parsedURL = parse_url($playlist);
250
251
        // 1 thing, assume playlist.
252
        if (count($parsedURL) === 1) {
253
            $this->type = static::TYPE_PLAYLIST;
254
            $this->id = $parsedURL['path'];
255
256
            return $this->cookies()->theme(static::THEME_LIGHT);
257
        }
258
259
        // both playlist types use list=
260
261
        $parsedQuery = explode('&', $parsedURL['query']);
262
        // Find list=
263 View Code Duplication
        foreach ($parsedQuery as $v) {
264
            if (substr(strtolower($v), 0, 5) == 'list=') {
265
                $this->type = static::TYPE_PLAYLIST;
266
                $this->id = substr($v, 5);
267
268
                return $this->cookies()->theme(static::THEME_LIGHT);
269
            }
270
        }
271
272
        // If we don't find list=, then its not a playlist.
273
        $this->source = $oldSource;
274
        throw new AudioPlayerException('Could not identify playlist');
275
    }
276
277
    /**
278
     * Get Source
279
     * Returns the source exactly as you fed it to us.
280
     * The source is only changed if setting it was successful.
281
     *
282
     * @return string
283
     */
284
    public function getSource()
285
    {
286
        return $this->source;
287
    }
288
289
    /**
290
     * Get Player Type.
291
     *
292
     * @return int
293
     */
294 4
    public function getType()
295
    {
296 4
        return $this->type;
297
    }
298
299
    public function isVideo()
300
    {
301
        return $this->getType() == static::TYPE_VIDEO;
302
    }
303
304 3
    public function isPlaylist()
305
    {
306 3
        return $this->getType() == static::TYPE_PLAYLIST;
307
    }
308
309
    /**
310
     * Get Player ID.
311
     *
312
     * @return string
313
     */
314
    public function getID()
315
    {
316
        return $this->id;
317
    }
318
319
    /**
320
     * Set Player Size.
321
     *
322
     *
323
     * @param int $size [SIZE_INVISIBLE | SIZE_TINY | SIZE_SMALL | SIZE_MEDIUM | SIZE_LARGE]
324
     *
325
     * @throws AudioPlayerException
326
     *
327
     * @return $this
328
     */
329 1
    public function size($size)
330
    {
331 1
        if (!$this->isValidSize($size)) {
332
            throw new AudioPlayerException('Invalid Size');
333
        }
334 1
        $this->size = $size;
335
336
        // Progress Bar & Time Code can not be used with Tiny/Invisible
337
        // Any other size (Small, Medium, Large) MUST have a Progress Bar
338 1
        if ($size == static::SIZE_TINY || $size == static::SIZE_INVISIBLE) {
339 1
            $this->progressBar(false)->timeCode(false);
340 1
        } else {
341
            $this->progressBar();
342
        }
343
344 1
        return $this;
345
    }
346
347 1
    protected function isValidSize($size)
348
    {
349 1
        return in_array(
350 1
            $size,
351 1
            [static::SIZE_INVISIBLE, static::SIZE_TINY, static::SIZE_SMALL, static::SIZE_MEDIUM, static::SIZE_LARGE]
352 1
        );
353
    }
354
355
    /**
356
     * Get Player Size.
357
     *
358
     * @return int
359
     */
360 3
    public function getSize()
361
    {
362 3
        return $this->size;
363
    }
364
365 1
    public function isTiny()
366
    {
367 1
        return $this->getSize() == static::SIZE_TINY;
368
    }
369
370 1
    public function isSmall()
371
    {
372 1
        return $this->getSize() == static::SIZE_SMALL;
373
    }
374
375 1
    public function isMedium()
376
    {
377 1
        return $this->getSize() == static::SIZE_MEDIUM;
378
    }
379
380 1
    public function isLarge()
381
    {
382 1
        return $this->getSize() == static::SIZE_LARGE;
383
    }
384
385
    /**
386
     * Set Player Invisible
387
     * Convenience function, since Invisibility is a SIZE.
388
     *
389
     * @return $this
390
     */
391 1
    public function invisible()
392
    {
393 1
        $this->size(static::SIZE_INVISIBLE);
394 1
    }
395
396
    /**
397
     * Get Player Invisibility Setting.
398
     *
399
     * @return bool
400
     */
401 1
    public function getInvisible()
402
    {
403 1
        return $this->getSize() == static::SIZE_INVISIBLE;
404
    }
405
406 1
    public function isInvisible()
407
    {
408 1
        return $this->getInvisible();
409
    }
410
411
    /**
412
     * Set Player Theme.
413
     *
414
     *
415
     * @param int $theme [THEME_LIGHT | THEME_DARK]
416
     *
417
     * @throws AudioPlayerException
418
     *
419
     * @return $this
420
     */
421 3
    public function theme($theme)
422
    {
423 3
        if ($this->isPlaylist() && $theme == static::THEME_DARK) {
424
            throw new AudioPlayerException('Playlists can not use the Dark Theme.  YouTube limitation.');
425
        }
426 3
        if ($theme != static::THEME_LIGHT && $theme != static::THEME_DARK) {
427 1
            throw new AudioPlayerException('Invalid Theme');
428
        }
429 2
        $this->theme = $theme;
430
431 2
        return $this;
432
    }
433
434
    /**
435
     * Get Player Theme.
436
     *
437
     * @return bool
438
     */
439 2
    public function getTheme()
440
    {
441 2
        return $this->theme;
442
    }
443
444
    /**
445
     * Set HD
446
     * Choose whether or not to force the player into HD.
447
     *
448
     * @param bool $useHD
449
     *
450
     * @return $this
451
     */
452 1
    public function hd($useHD = true)
453
    {
454 1
        if ($useHD) {
455 1
            $this->hd = true;
456 1
        } else {
457 1
            $this->hd = false;
458
        }
459
460 1
        return $this;
461
    }
462
463
    /**
464
     * Get HD Setting.
465
     *
466
     * @return bool
467
     */
468 1
    public function getHD()
469
    {
470 1
        return $this->hd;
471
    }
472
473 1
    public function isHD()
474
    {
475 1
        return $this->getHD();
476
    }
477
478
    /**
479
     * Set Autoplay
480
     * Choose whether or not to automatically play the video when it loads
481
     * Please don't use this.  You'll make me sad.
482
     *
483
     * @param bool $autoplay
484
     *
485
     * @return $this
486
     */
487 1
    public function autoplay($autoplay = true)
488
    {
489 1
        if ($autoplay) {
490 1
            $this->autoplay = true;
491 1
        } else {
492 1
            $this->autoplay = false;
493
        }
494
495 1
        return $this;
496
    }
497
498
    /**
499
     * Get Autoplay Setting.
500
     *
501
     * @return bool
502
     */
503 1
    public function getAutoplay()
504
    {
505 1
        return $this->autoplay;
506
    }
507
508 1
    public function willAutoplay()
509
    {
510 1
        return $this->getAutoplay();
511
    }
512
513
    /**
514
     * Set JSApi
515
     * Choose whether or not to allow access via the YouTube JavaScript API.
516
     *
517
     * @param bool $useJSAPI
518
     *
519
     * @return $this
520
     */
521 1
    public function jsAPI($useJSAPI = true)
522
    {
523 1
        if ($useJSAPI) {
524 1
            $this->jsapi = true;
525 1
        } else {
526 1
            $this->jsapi = false;
527
        }
528
529 1
        return $this;
530
    }
531
532
    /**
533
     * Get JavaScript API Setting.
534
     *
535
     * @return bool
536
     */
537 1
    public function getJSAPI()
538
    {
539 1
        return $this->jsapi;
540
    }
541
542 1
    public function canUseJSAPI()
543
    {
544 1
        return $this->getJSAPI();
545
    }
546
547 1
    public function canUseJavaScriptAPI()
548
    {
549 1
        return $this->getJSAPI();
550
    }
551
552
    /**
553
     * Set Loop
554
     * Choose whether or not to loop once the video/playlist is over.
555
     *
556
     * @param bool $loop
557
     *
558
     * @return $this
559
     */
560 1
    public function loop($loop = true)
561
    {
562 1
        if ($loop) {
563 1
            $this->loop = true;
564 1
        } else {
565 1
            $this->loop = false;
566
        }
567
568 1
        return $this;
569
    }
570
571
    /**
572
     * Get Loop Setting.
573
     *
574
     * @return bool
575
     */
576 1
    public function getLoop()
577
    {
578 1
        return $this->loop;
579
    }
580
581 1
    public function willLoop()
582
    {
583 1
        return $this->getLoop();
584
    }
585
586
    /**
587
     * Set Progress Bar
588
     * Choose whether or not to display the progress bar.
589
     *
590
     * @param bool $useProgressBar
591
     *
592
     * @return $this
593
     */
594 3 View Code Duplication
    public function progressBar($useProgressBar = true)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
595
    {
596 3
        if ($useProgressBar) {
597 2
            $this->progressbar = true;
598 2
        } else {
599 2
            $this->progressbar = false;
600
        }
601
602
        // If they set this true after saying they want tiny, they actually want small.
603 3
        if ($useProgressBar && $this->getSize() == static::SIZE_TINY) {
604
            $this->size(static::SIZE_SMALL);
605
        }
606
        // If they set this true after saying they want invisible, they're being silly and I refuse to handle it.
607
608 3
        return $this;
609
    }
610
611
    /**
612
     * Get Progress Bar Setting.
613
     *
614
     * @return bool
615
     */
616 1
    public function getProgressBar()
617
    {
618 1
        return $this->progressbar;
619
    }
620
621 1
    public function hasProgressBar()
622
    {
623 1
        return $this->getProgressBar();
624
    }
625
626
    /**
627
     * Set Time Code
628
     * Choose whether or not to display the time code.  Requires Progress Bar.
629
     *
630
     * @param bool $useTimeCode
631
     *
632
     * @return $this
633
     */
634 2 View Code Duplication
    public function timeCode($useTimeCode = true)
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
635
    {
636 2
        if ($useTimeCode) {
637
            // Requires Progress Bar.  Sorry.
638 1
            $this->progressBar();
639 1
            $this->timecode = true;
640 1
        } else {
641 2
            $this->timecode = false;
642
        }
643
644
        // If they set this true after saying they want tiny, they actually want small.
645 2
        if ($this->progressbar && $this->getSize() == static::SIZE_TINY) {
646
            $this->size(static::SIZE_SMALL);
647
        }
648
        // If they set this true after saying they want invisible, they're being silly and I refuse to handle it.
649
650 2
        return $this;
651
    }
652
653
    /**
654
     * Get Time Code Setting.
655
     *
656
     * @return bool
657
     */
658 1
    public function getTimeCode()
659
    {
660 1
        return $this->timecode;
661
    }
662
663 1
    public function hasTimeCode()
664
    {
665 1
        return $this->getTimeCode();
666
    }
667
668
    /**
669
     * Set Cookies
670
     * Choose whether or not to allow YouTube to collect cookies.
671
     *
672
     * @param bool $useCookies
673
     *
674
     * @throws AudioPlayerException
675
     *
676
     * @return $this
677
     */
678 1
    public function cookies($useCookies = true)
679
    {
680 1
        if (!$useCookies && $this->getType() == static::TYPE_PLAYLIST) {
681
            throw new AudioPlayerException('Can not disable cookies with playlists.  YouTube limitation.');
682
        }
683
684 1
        if ($useCookies) {
685 1
            $this->cookies = true;
686 1
        } else {
687 1
            $this->cookies = false;
688
        }
689
690 1
        return $this;
691
    }
692
693
    /**
694
     * Get Cookie Setting.
695
     *
696
     * @return bool
697
     */
698 1
    public function getCookies()
699
    {
700 1
        return $this->cookies;
701
    }
702
703 1
    public function willUseCookies()
704
    {
705 1
        return $this->getCookies();
706
    }
707
708
    /**
709
     * Set HTTPS
710
     * Choose whether to use HTTPs or HTTP.
711
     *
712
     * @param bool $useHTTPS
713
     *
714
     * @return $this
715
     */
716 1
    public function https($useHTTPS = true)
717
    {
718 1
        if ($useHTTPS) {
719 1
            $this->https = true;
720 1
        } else {
721 1
            $this->https = false;
722
        }
723
724 1
        return $this;
725
    }
726
727
    /**
728
     * Get HTTPS Setting.
729
     *
730
     * @return bool
731
     */
732 1
    public function getHTTPS()
733
    {
734 1
        return $this->https;
735
    }
736
737 1
    public function isHTTPS()
738
    {
739 1
        return $this->https;
740
    }
741
742 1
    public function isHTTP()
743
    {
744 1
        return !$this->https;
745
    }
746
747
    /**
748
     * Get Height (px).
749
     *
750
     * @return int
751
     */
752
    public function getHeight()
753
    {
754
        if ($this->getSize() == static::SIZE_INVISIBLE) {
755
            return 1;
756
        }
757
758
        return 25;
759
    }
760
761
    /**
762
     * Get Width (px).
763
     *
764
     * @return int
765
     */
766
    public function getWidth()
767
    {
768
        if ($this->getSize() == static::SIZE_INVISIBLE) {
769
            return 1;
770
        }
771
        if ($this->getSize() == static::SIZE_TINY) {
772
            return 30;
773
        }
774
775
        $modifier = 0;
776
        if ($this->getTimeCode()) {
777
            $modifier = 75;
778
        }
779
780
        if ($this->getSize() == static::SIZE_SMALL) {
781
            return 150 + $modifier;
782
        }
783
        if ($this->getSize() == static::SIZE_MEDIUM) {
784
            return 187 + $modifier;
785
        }
786
        // static::SIZE_LARGE
787
        return 224 + $modifier;
788
    }
789
790
    /**
791
     * Get Embed URL.
792
     *
793
     * @param bool $encode
794
     *
795
     * @return string
796
     */
797
    public function getEmbedURL($encode = true)
798
    {
799
        $url = '';
800
801
        // PROTOCOL
802
        if ($this->getHTTPS()) {
803
            $url .= 'https://';
804
        } else {
805
            $url .= 'http://';
806
        }
807
808
        // DOMAIN
809
        if ($this->getCookies()) {
810
            $url .= 'www.youtube.com';
811
        } else {
812
            $url .= 'www.youtube-nocookie.com';
813
        }
814
815
        // PATH
816
        if ($this->isVideo()) {
817
            $url .= '/v/';
818
        } else {
819
            $url .= '/p/';
820
        }
821
822
        // ID
823
        if ($this->isVideo()) {
824
            $url .= $this->getID();
825
        } else {
826
            $url .= substr($this->getID(), 2);
827
        } // Playlists start with PL but YouTube doesn't want that
828
829
        // Build Query String
830
        $query = [];
831
        $query['version'] = 2;
832
        if ($this->getAutoplay()) {
833
            $query['autoplay'] = 1;
834
        }
835
        if ($this->getLoop()) {
836
            $query['loop'] = 1;
837
        }
838
        if ($this->getJSAPI()) {
839
            $query['enablejsapi'] = 1;
840
        }
841
        if ($this->isHD()) {
842
            $query['hd'] = 1;
843
        }
844
        $query['theme'] = $this->getTheme();
845
846
        $seperator = $encode ? '&amp;' : '&';
847
        $url .= '?'.http_build_query($query, $seperator);
848
849
        return $url;
850
    }
851
852
    /**
853
     * Render valid XHTML.
854
     *
855
     * @param bool $return Return the HTML instead of echoing it.
856
     *
857
     * @return string|bool
858
     */
859
    public function render($return = false)
860
    {
861
        // Build the string
862
        $html = '<object type="application/x-shockwave-flash"';
863
        $html .= ' width="'.$this->getWidth().'"';
864
        $html .= ' height="'.$this->getHeight().'"';
865
        $html .= ' data="'.$this->getEmbedURL().'"';
866
        if ($this->isInvisible()) {
867
            $html .= ' style="visibility:hidden;display:inline;"';
868
        }
869
        $html .= '>';
870
        $html .= '<param name="movie" value="'.$this->getEmbedURL().'" />';
871
        $html .= '<param name="wmode" value="transparent" />';
872
        $html .= '</object>';
873
874
        if ($return) {
875
            return $html;
876
        }
877
        echo $html;
878
879
        return true;
880
    }
881
}
882