@@ -111,6 +111,9 @@ class Video extends ObjectYPT
111111 const ASPECT_RATIO_VERTICAL = '9:16 ' ;
112112 const ASPECT_RATIO_HORIZONTAL = '16:9 ' ;
113113
114+ /** Sentinel value: tells setVideo_password() to leave the stored password unchanged. */
115+ const PASSWORD_KEEP = '__keep__ ' ;
116+
114117
115118 const SORT_TYPE_CHANNELSUGGESTED = 'channelSuggested ' ;
116119 const SORT_TYPE_SUGGESTED = 'suggested ' ;
@@ -520,10 +523,44 @@ public function getVideo_password()
520523 return trim ($ this ->video_password );
521524 }
522525
526+ /**
527+ * Returns a bcrypt hash of the given plaintext password.
528+ * Already-hashed values (bcrypt prefix '$2') are returned as-is.
529+ * Empty string is returned unchanged (means "no password").
530+ */
531+ public static function hashVideoPassword (string $ plaintext ): string
532+ {
533+ if ($ plaintext === '' || substr ($ plaintext , 0 , 2 ) === '$2 ' ) {
534+ return $ plaintext ;
535+ }
536+ return password_hash ($ plaintext , PASSWORD_BCRYPT );
537+ }
538+
539+ /**
540+ * Verifies a plaintext password against the stored value.
541+ * Handles both bcrypt hashes (modern) and legacy plaintext (backward-compat).
542+ */
543+ public static function verifyVideoPassword (string $ entered , string $ stored ): bool
544+ {
545+ if ($ stored === '' ) {
546+ return true ;
547+ }
548+ if (substr ($ stored , 0 , 2 ) === '$2 ' ) {
549+ return password_verify ($ entered , $ stored );
550+ }
551+ // Legacy plaintext — direct comparison until owner re-saves
552+ return $ entered === $ stored ;
553+ }
554+
523555 public function setVideo_password ($ video_password )
524556 {
557+ $ video_password = trim ($ video_password );
558+ if ($ video_password === self ::PASSWORD_KEEP ) {
559+ return ;
560+ }
561+ $ video_password = self ::hashVideoPassword ($ video_password );
525562 AVideoPlugin::onVideoSetVideo_password ($ this ->id , $ this ->video_password , $ video_password );
526- $ this ->video_password = trim ( $ video_password) ;
563+ $ this ->video_password = $ video_password ;
527564 }
528565
529566 public function save ($ updateVideoGroups = false , $ allowOfflineUser = false )
@@ -2289,17 +2326,7 @@ static function getInfo($row, $getStatistcs = false)
22892326 if (!empty ($ obj ['userExternalOptions ' ]) && is_string ($ obj ['userExternalOptions ' ])) {
22902327 $ obj ['userExternalOptions ' ] = User::decodeExternalOption ($ obj ['userExternalOptions ' ]);
22912328 }
2292- $ obj = cleanUpRowFromDatabase ($ obj );
2293-
2294- if (!self ::canEdit ($ obj ['id ' ])) {
2295- if (!empty ($ rowOriginal ['video_password ' ])) {
2296- $ obj ['video_password ' ] = '1 ' ;
2297- } else {
2298- $ obj ['video_password ' ] = '0 ' ;
2299- }
2300- } else {
2301- $ obj ['video_password ' ] = empty ($ rowOriginal ['video_password ' ]) ? '' : $ rowOriginal ['video_password ' ];
2302- }
2329+ $ obj = cleanUpRowFromDatabase ($ obj ); // video_password is reduced to '' / '1' flag inside
23032330 if (self ::forceAudio ()) {
23042331 $ obj ['type ' ] = 'audio ' ;
23052332 } else if (self ::forceArticle ()) {
@@ -2403,16 +2430,7 @@ static function getInfoPersonal($row)
24032430 return object_to_array ($ cache );
24042431 }
24052432
2406- $ row = cleanUpRowFromDatabase ($ row );
2407- if (!self ::canEdit ($ row ['id ' ])) {
2408- if (!empty ($ rowOriginal ['video_password ' ])) {
2409- $ row ['video_password ' ] = '1 ' ;
2410- } else {
2411- $ row ['video_password ' ] = '0 ' ;
2412- }
2413- } else {
2414- $ row ['video_password ' ] = empty ($ rowOriginal ['video_password ' ]) ? '' : $ rowOriginal ['video_password ' ];
2415- }
2433+ $ row = cleanUpRowFromDatabase ($ row ); // video_password is reduced to '' / '1' flag inside
24162434 TimeLogEnd ($ timeLogName , __LINE__ , $ TimeLogLimit );
24172435 $ row ['isFavorite ' ] = self ::isFavorite ($ row ['id ' ]);
24182436 TimeLogEnd ($ timeLogName , __LINE__ , $ TimeLogLimit );
0 commit comments