@frameset/plex-player 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -66,13 +66,14 @@
66
66
  color: var(--plex-text-primary);
67
67
  }
68
68
 
69
- .plex-player.fullscreen {
69
+ .plex-player.plex-fullscreen {
70
70
  position: fixed;
71
71
  top: 0;
72
72
  left: 0;
73
73
  width: 100vw;
74
74
  height: 100vh;
75
75
  z-index: 9999;
76
+ border-radius: 0;
76
77
  }
77
78
 
78
79
  /* Video Container */
@@ -578,56 +579,38 @@
578
579
  }
579
580
 
580
581
  /* =====================================================
581
- PLEX Video Player - Controls Styles
582
+ PLEX Video Player - Controls Styles (NPM Package)
582
583
  ===================================================== */
583
584
 
584
- /* Top Controls Bar */
585
- .top-controls {
586
- position: absolute;
587
- top: 0;
588
- left: 0;
585
+ /* Video Element */
586
+ .plex-video {
589
587
  width: 100%;
590
- display: flex;
591
- align-items: center;
592
- justify-content: space-between;
593
- padding: 16px 20px;
594
- background: var(--plex-gradient-top);
595
- z-index: var(--plex-z-controls);
596
- transition: all var(--plex-transition-normal);
597
- }
598
-
599
- .top-left,
600
- .top-right {
601
- display: flex;
602
- align-items: center;
603
- gap: 8px;
604
- }
605
-
606
- .video-title {
607
- font-size: 16px;
608
- font-weight: 600;
609
- color: var(--plex-text-primary);
610
- margin-left: 8px;
611
- max-width: 400px;
612
- white-space: nowrap;
613
- overflow: hidden;
614
- text-overflow: ellipsis;
588
+ height: 100%;
589
+ object-fit: contain;
590
+ background: #000;
615
591
  }
616
592
 
617
- /* Bottom Controls */
618
- .bottom-controls {
593
+ /* Controls Container */
594
+ .plex-controls {
619
595
  position: absolute;
620
596
  bottom: 0;
621
597
  left: 0;
622
- width: 100%;
598
+ right: 0;
623
599
  padding: 0 16px 16px;
624
600
  background: var(--plex-gradient);
625
601
  z-index: var(--plex-z-controls);
626
- transition: all var(--plex-transition-normal);
602
+ transition: opacity var(--plex-transition-normal), visibility var(--plex-transition-normal);
603
+ opacity: 1;
604
+ visibility: visible;
605
+ }
606
+
607
+ .plex-player:not(.plex-controls-visible) .plex-controls {
608
+ opacity: 0;
609
+ visibility: hidden;
627
610
  }
628
611
 
629
612
  /* Progress Container */
630
- .progress-container {
613
+ .plex-progress-container {
631
614
  position: relative;
632
615
  width: 100%;
633
616
  height: 24px;
@@ -637,7 +620,7 @@
637
620
  padding: 8px 0;
638
621
  }
639
622
 
640
- .progress-bar {
623
+ .plex-progress-bar {
641
624
  position: relative;
642
625
  width: 100%;
643
626
  height: 4px;
@@ -647,11 +630,11 @@
647
630
  transition: height var(--plex-transition-fast);
648
631
  }
649
632
 
650
- .progress-container:hover .progress-bar {
633
+ .plex-progress-container:hover .plex-progress-bar {
651
634
  height: 6px;
652
635
  }
653
636
 
654
- .progress-buffered {
637
+ .plex-progress-buffered {
655
638
  position: absolute;
656
639
  top: 0;
657
640
  left: 0;
@@ -659,9 +642,10 @@
659
642
  background: rgba(255, 255, 255, 0.35);
660
643
  border-radius: 2px;
661
644
  pointer-events: none;
645
+ width: 0;
662
646
  }
663
647
 
664
- .progress-played {
648
+ .plex-progress-played {
665
649
  position: absolute;
666
650
  top: 0;
667
651
  left: 0;
@@ -669,9 +653,10 @@
669
653
  background: var(--plex-primary);
670
654
  border-radius: 2px;
671
655
  pointer-events: none;
656
+ width: 0;
672
657
  }
673
658
 
674
- .progress-handle {
659
+ .plex-progress-handle {
675
660
  position: absolute;
676
661
  top: 50%;
677
662
  left: 0;
@@ -685,120 +670,63 @@
685
670
  pointer-events: none;
686
671
  }
687
672
 
688
- .progress-container:hover .progress-handle,
689
- .progress-container.dragging .progress-handle {
673
+ .plex-progress-container:hover .plex-progress-handle {
690
674
  transform: translate(-50%, -50%) scale(1);
691
675
  }
692
676
 
693
- .progress-container.dragging .progress-bar {
694
- height: 6px;
695
- }
696
-
697
- /* Progress Tooltip */
698
- .progress-tooltip {
677
+ /* Progress Preview */
678
+ .plex-progress-preview {
699
679
  position: absolute;
700
680
  bottom: 100%;
701
681
  left: 0;
682
+ margin-bottom: 8px;
702
683
  padding: 6px 10px;
703
684
  background: var(--plex-bg-dark);
704
685
  border-radius: var(--plex-radius-sm);
705
- font-size: 13px;
706
- font-weight: 500;
707
- white-space: nowrap;
708
686
  opacity: 0;
709
687
  visibility: hidden;
710
- transform: translateX(-50%) translateY(4px);
711
- transition: all var(--plex-transition-fast);
688
+ transform: translateX(-50%);
689
+ transition: opacity var(--plex-transition-fast);
712
690
  pointer-events: none;
713
691
  z-index: 50;
714
692
  }
715
693
 
716
- .progress-container:hover .progress-tooltip {
694
+ .plex-progress-container:hover .plex-progress-preview {
717
695
  opacity: 1;
718
696
  visibility: visible;
719
- transform: translateX(-50%) translateY(-4px);
720
- }
721
-
722
- /* Hide tooltip when preview is visible */
723
- .progress-container:hover .progress-preview[style*="display: flex"] ~ .progress-tooltip,
724
- .progress-preview:not([style*="display: none"]) + .progress-tooltip {
725
- opacity: 0;
726
- visibility: hidden;
727
697
  }
728
698
 
729
- /* Progress Preview (Thumbnail) */
730
- .progress-preview {
731
- position: absolute;
732
- bottom: calc(100% + 16px);
733
- left: 0;
734
- display: none;
735
- flex-direction: column;
736
- align-items: center;
737
- z-index: 100;
738
- pointer-events: none;
739
- background: rgba(0, 0, 0, 0.9);
740
- padding: 4px;
741
- border-radius: var(--plex-radius-sm);
742
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
743
- }
744
-
745
- #preview-canvas {
746
- width: 160px;
747
- height: 90px;
748
- background: var(--plex-bg-dark);
749
- border: 2px solid var(--plex-primary);
750
- border-radius: var(--plex-radius-sm);
751
- object-fit: contain;
752
- }
753
-
754
- .preview-time {
755
- margin-top: 6px;
756
- padding: 4px 10px;
757
- background: var(--plex-primary);
758
- color: #000;
759
- border-radius: var(--plex-radius-sm);
760
- font-size: 12px;
761
- font-weight: 700;
762
- }
763
-
764
- /* Time Display */
765
- .time-display {
766
- display: flex;
767
- align-items: center;
768
- gap: 4px;
769
- padding: 8px 0;
699
+ .plex-preview-time {
770
700
  font-size: 13px;
771
- font-weight: 500;
701
+ font-weight: 600;
772
702
  color: var(--plex-text-primary);
703
+ white-space: nowrap;
773
704
  }
774
705
 
775
- .time-separator {
776
- color: var(--plex-text-secondary);
777
- }
778
-
779
- /* Main Controls Row */
780
- .main-controls {
706
+ /* Controls Bar */
707
+ .plex-controls-bar {
781
708
  display: flex;
782
709
  align-items: center;
783
710
  justify-content: space-between;
784
- margin-top: 4px;
711
+ margin-top: 8px;
785
712
  }
786
713
 
787
- .controls-left,
788
- .controls-right {
714
+ .plex-controls-left,
715
+ .plex-controls-right {
789
716
  display: flex;
790
717
  align-items: center;
791
718
  gap: 4px;
792
719
  }
793
720
 
794
721
  /* Control Button */
795
- .control-btn {
722
+ .plex-btn {
796
723
  position: relative;
797
724
  display: flex;
798
725
  align-items: center;
799
726
  justify-content: center;
800
727
  width: 40px;
801
728
  height: 40px;
729
+ padding: 0;
802
730
  background: transparent;
803
731
  border: none;
804
732
  border-radius: var(--plex-radius-sm);
@@ -807,280 +735,304 @@
807
735
  color: var(--plex-text-primary);
808
736
  }
809
737
 
810
- .control-btn:hover {
738
+ .plex-btn:hover {
811
739
  background: rgba(255, 255, 255, 0.1);
812
740
  }
813
741
 
814
- .control-btn:active {
742
+ .plex-btn:active {
815
743
  transform: scale(0.95);
816
744
  }
817
745
 
818
- .control-btn .material-icons {
819
- font-size: 24px;
820
- }
821
-
822
- .control-btn.active {
823
- color: var(--plex-primary);
824
- }
825
-
826
- /* Cast Button */
827
- #cast-btn {
828
- transition: all var(--plex-transition-fast);
746
+ .plex-btn svg {
747
+ width: 24px;
748
+ height: 24px;
749
+ fill: currentColor;
829
750
  }
830
751
 
831
- #cast-btn.active {
832
- color: var(--plex-primary);
833
- animation: pulse-cast 2s ease-in-out infinite;
752
+ .plex-btn:focus-visible {
753
+ outline: 2px solid var(--plex-primary);
754
+ outline-offset: 2px;
834
755
  }
835
756
 
836
- #cast-btn.hidden {
757
+ /* Play Button States */
758
+ .plex-player.plex-playing .plex-play-btn .plex-icon-play {
837
759
  display: none;
838
760
  }
839
761
 
840
- @keyframes pulse-cast {
841
- 0%, 100% { opacity: 1; }
842
- 50% { opacity: 0.6; }
762
+ .plex-player.plex-playing .plex-play-btn .plex-icon-pause {
763
+ display: block;
843
764
  }
844
765
 
845
- /* Play Button (Larger) */
846
- .play-btn {
847
- width: 48px;
848
- height: 48px;
766
+ .plex-player:not(.plex-playing) .plex-play-btn .plex-icon-play {
767
+ display: block;
849
768
  }
850
769
 
851
- .play-btn .material-icons {
852
- font-size: 32px;
853
- }
854
-
855
- /* Speed Text */
856
- .speed-text {
857
- font-size: 14px;
858
- font-weight: 600;
859
- min-width: 28px;
770
+ .plex-player:not(.plex-playing) .plex-play-btn .plex-icon-pause {
771
+ display: none;
860
772
  }
861
773
 
862
- /* Volume Control */
863
- .volume-control {
774
+ /* Volume Container */
775
+ .plex-volume-container {
864
776
  display: flex;
865
777
  align-items: center;
866
778
  gap: 4px;
867
779
  }
868
780
 
869
- .volume-slider-container {
781
+ .plex-volume-slider {
870
782
  position: relative;
871
783
  width: 0;
872
784
  overflow: hidden;
873
785
  transition: width var(--plex-transition-normal);
874
786
  }
875
787
 
876
- .volume-control:hover .volume-slider-container {
788
+ .plex-volume-container:hover .plex-volume-slider {
877
789
  width: 80px;
878
790
  }
879
791
 
880
- .volume-slider {
881
- -webkit-appearance: none;
882
- appearance: none;
792
+ .plex-volume-track {
793
+ position: relative;
883
794
  width: 80px;
884
795
  height: 4px;
885
796
  background: rgba(255, 255, 255, 0.2);
886
797
  border-radius: 2px;
887
- outline: none;
888
798
  cursor: pointer;
889
799
  }
890
800
 
891
- .volume-slider::-webkit-slider-thumb {
892
- -webkit-appearance: none;
893
- appearance: none;
894
- width: 14px;
895
- height: 14px;
801
+ .plex-volume-level {
802
+ position: absolute;
803
+ top: 0;
804
+ left: 0;
805
+ height: 100%;
806
+ background: var(--plex-primary);
807
+ border-radius: 2px;
808
+ pointer-events: none;
809
+ width: 100%;
810
+ }
811
+
812
+ .plex-volume-handle {
813
+ position: absolute;
814
+ top: 50%;
815
+ right: 0;
816
+ width: 12px;
817
+ height: 12px;
896
818
  background: var(--plex-primary);
897
819
  border-radius: 50%;
820
+ transform: translateY(-50%);
898
821
  cursor: pointer;
899
- transition: transform var(--plex-transition-fast);
822
+ opacity: 0;
823
+ transition: opacity var(--plex-transition-fast);
900
824
  }
901
825
 
902
- .volume-slider::-webkit-slider-thumb:hover {
903
- transform: scale(1.2);
826
+ .plex-volume-container:hover .plex-volume-handle {
827
+ opacity: 1;
904
828
  }
905
829
 
906
- .volume-slider::-moz-range-thumb {
907
- width: 14px;
908
- height: 14px;
909
- background: var(--plex-primary);
910
- border: none;
911
- border-radius: 50%;
912
- cursor: pointer;
830
+ /* Volume Button States */
831
+ .plex-player.plex-muted .plex-icon-volume-high,
832
+ .plex-player.plex-muted .plex-icon-volume-low {
833
+ display: none;
913
834
  }
914
835
 
915
- .volume-slider-fill {
916
- position: absolute;
917
- top: 50%;
918
- left: 0;
919
- height: 4px;
920
- background: var(--plex-primary);
921
- border-radius: 2px;
922
- transform: translateY(-50%);
923
- pointer-events: none;
836
+ .plex-player.plex-muted .plex-icon-volume-mute {
837
+ display: block;
924
838
  }
925
839
 
926
- /* Chapters Markers (on progress bar) */
927
- .chapter-marker {
928
- position: absolute;
929
- top: 0;
930
- width: 3px;
931
- height: 100%;
932
- background: rgba(255, 255, 255, 0.8);
933
- border-radius: 1px;
934
- transform: translateX(-50%);
935
- cursor: pointer;
936
- z-index: 2;
840
+ .plex-player:not(.plex-muted) .plex-icon-volume-mute {
841
+ display: none;
937
842
  }
938
843
 
939
- .chapter-marker:hover {
940
- background: var(--plex-primary);
844
+ .plex-player:not(.plex-muted) .plex-icon-volume-high {
845
+ display: block;
941
846
  }
942
847
 
943
- /* Skip Intro Button */
944
- .skip-intro-btn {
848
+ .plex-player:not(.plex-muted) .plex-icon-volume-low {
849
+ display: none;
850
+ }
851
+
852
+ /* Time Display */
853
+ .plex-time {
854
+ display: flex;
855
+ align-items: center;
856
+ gap: 4px;
857
+ margin-left: 8px;
858
+ font-size: 13px;
859
+ font-weight: 500;
860
+ color: var(--plex-text-primary);
861
+ }
862
+
863
+ .plex-time-current,
864
+ .plex-time-duration {
865
+ font-variant-numeric: tabular-nums;
866
+ }
867
+
868
+ .plex-time-separator {
869
+ color: var(--plex-text-secondary);
870
+ }
871
+
872
+ /* Fullscreen Button States */
873
+ .plex-player.plex-fullscreen .plex-icon-fullscreen {
874
+ display: none;
875
+ }
876
+
877
+ .plex-player.plex-fullscreen .plex-icon-fullscreen-exit {
878
+ display: block;
879
+ }
880
+
881
+ .plex-player:not(.plex-fullscreen) .plex-icon-fullscreen {
882
+ display: block;
883
+ }
884
+
885
+ .plex-player:not(.plex-fullscreen) .plex-icon-fullscreen-exit {
886
+ display: none;
887
+ }
888
+
889
+ /* Big Play Button */
890
+ .plex-big-play {
945
891
  position: absolute;
946
- right: 20px;
947
- bottom: 100px;
892
+ top: 50%;
893
+ left: 50%;
894
+ transform: translate(-50%, -50%) scale(1);
895
+ width: 72px;
896
+ height: 72px;
897
+ background: var(--plex-primary);
898
+ border-radius: 50%;
948
899
  display: flex;
949
900
  align-items: center;
950
- gap: 8px;
951
- padding: 12px 20px;
952
- background: rgba(255, 255, 255, 0.9);
953
- color: #000;
954
- border: none;
955
- border-radius: var(--plex-radius-md);
956
- font-size: 14px;
957
- font-weight: 600;
901
+ justify-content: center;
958
902
  cursor: pointer;
959
- z-index: var(--plex-z-controls);
903
+ z-index: var(--plex-z-overlay);
960
904
  transition: all var(--plex-transition-normal);
905
+ box-shadow: 0 4px 30px rgba(229, 160, 13, 0.4);
961
906
  }
962
907
 
963
- .skip-intro-btn:hover {
964
- background: #fff;
965
- transform: scale(1.02);
908
+ .plex-big-play:hover {
909
+ transform: translate(-50%, -50%) scale(1.1);
910
+ background: var(--plex-primary-hover);
966
911
  }
967
912
 
968
- .skip-intro-btn .material-icons {
969
- font-size: 20px;
913
+ .plex-big-play svg {
914
+ width: 40px;
915
+ height: 40px;
916
+ fill: #000;
917
+ margin-left: 4px;
970
918
  }
971
919
 
972
- /* Double Tap Ripple Effect */
973
- .tap-ripple {
920
+ .plex-player.plex-playing .plex-big-play {
921
+ opacity: 0;
922
+ visibility: hidden;
923
+ transform: translate(-50%, -50%) scale(0.8);
924
+ }
925
+
926
+ /* Loader */
927
+ .plex-loader {
974
928
  position: absolute;
975
- width: 100px;
976
- height: 100px;
929
+ top: 50%;
930
+ left: 50%;
931
+ transform: translate(-50%, -50%);
932
+ z-index: var(--plex-z-overlay);
933
+ display: none;
934
+ }
935
+
936
+ .plex-player.plex-loading .plex-loader {
937
+ display: block;
938
+ }
939
+
940
+ .plex-player.plex-loading .plex-big-play {
941
+ display: none;
942
+ }
943
+
944
+ .plex-spinner {
945
+ width: 48px;
946
+ height: 48px;
947
+ border: 4px solid rgba(255, 255, 255, 0.2);
948
+ border-top-color: var(--plex-primary);
977
949
  border-radius: 50%;
978
- background: rgba(255, 255, 255, 0.3);
979
- transform: translate(-50%, -50%) scale(0);
980
- animation: ripple 0.5s ease-out forwards;
981
- pointer-events: none;
950
+ animation: plex-spin 1s linear infinite;
982
951
  }
983
952
 
984
- @keyframes ripple {
953
+ @keyframes plex-spin {
985
954
  to {
986
- transform: translate(-50%, -50%) scale(2);
987
- opacity: 0;
955
+ transform: rotate(360deg);
988
956
  }
989
957
  }
990
958
 
991
- /* Responsive Controls */
959
+ /* Responsive */
992
960
  @media (max-width: 768px) {
993
- .top-controls {
994
- padding: 12px 16px;
995
- }
996
-
997
- .video-title {
998
- font-size: 14px;
999
- max-width: 200px;
1000
- }
1001
-
1002
- .bottom-controls {
961
+ .plex-controls {
1003
962
  padding: 0 12px 12px;
1004
963
  }
1005
964
 
1006
- .control-btn {
965
+ .plex-btn {
1007
966
  width: 36px;
1008
967
  height: 36px;
1009
968
  }
1010
969
 
1011
- .control-btn .material-icons {
1012
- font-size: 22px;
1013
- }
1014
-
1015
- .play-btn {
1016
- width: 44px;
1017
- height: 44px;
970
+ .plex-btn svg {
971
+ width: 22px;
972
+ height: 22px;
1018
973
  }
1019
974
 
1020
- .play-btn .material-icons {
1021
- font-size: 28px;
1022
- }
1023
-
1024
- .time-display {
975
+ .plex-time {
1025
976
  font-size: 12px;
1026
977
  }
1027
978
 
1028
- .speed-text {
1029
- font-size: 12px;
979
+ .plex-big-play {
980
+ width: 60px;
981
+ height: 60px;
1030
982
  }
1031
983
 
1032
- #preview-canvas {
1033
- width: 120px;
1034
- height: 68px;
984
+ .plex-big-play svg {
985
+ width: 32px;
986
+ height: 32px;
1035
987
  }
1036
988
  }
1037
989
 
1038
990
  @media (max-width: 480px) {
1039
- .controls-left,
1040
- .controls-right {
991
+ .plex-controls-left,
992
+ .plex-controls-right {
1041
993
  gap: 0;
1042
994
  }
1043
995
 
1044
- .control-btn {
996
+ .plex-btn {
1045
997
  width: 32px;
1046
998
  height: 32px;
1047
999
  }
1048
1000
 
1049
- .control-btn .material-icons {
1050
- font-size: 20px;
1001
+ .plex-btn svg {
1002
+ width: 20px;
1003
+ height: 20px;
1051
1004
  }
1052
1005
 
1053
- .video-title {
1054
- max-width: 140px;
1055
- font-size: 13px;
1006
+ .plex-prev-btn,
1007
+ .plex-next-btn,
1008
+ .plex-cast-btn {
1009
+ display: none;
1056
1010
  }
1057
1011
 
1058
- /* Hide some buttons on very small screens */
1059
- #cast-btn,
1060
- #quality-btn {
1061
- display: none;
1012
+ .plex-big-play {
1013
+ width: 56px;
1014
+ height: 56px;
1015
+ }
1016
+
1017
+ .plex-big-play svg {
1018
+ width: 28px;
1019
+ height: 28px;
1062
1020
  }
1063
1021
  }
1064
1022
 
1065
- /* Hover/Focus States for Accessibility */
1066
- .control-btn:focus-visible {
1067
- outline: 2px solid var(--plex-primary);
1068
- outline-offset: 2px;
1069
- }
1070
-
1071
- .progress-container:focus-visible {
1072
- outline: 2px solid var(--plex-primary);
1073
- outline-offset: 2px;
1023
+ /* Controls Visibility */
1024
+ .plex-player.plex-controls-visible .plex-controls {
1025
+ opacity: 1;
1026
+ visibility: visible;
1074
1027
  }
1075
1028
 
1076
- /* Disabled State */
1077
- .control-btn:disabled {
1078
- opacity: 0.4;
1079
- cursor: not-allowed;
1029
+ /* Cursor hiding when controls hidden */
1030
+ .plex-player:not(.plex-controls-visible) {
1031
+ cursor: none;
1080
1032
  }
1081
1033
 
1082
- .control-btn:disabled:hover {
1083
- background: transparent;
1034
+ .plex-player:not(.plex-controls-visible) .plex-video {
1035
+ cursor: none;
1084
1036
  }
1085
1037
 
1086
1038
  /* =====================================================