refactor: update player handling and dashboard display in client and server

This commit is contained in:
2025-09-04 10:32:59 +02:00
parent b567187a77
commit edaf9ea94e
4 changed files with 161 additions and 99 deletions

View File

@@ -101,22 +101,22 @@
"mbid": "e8bdfb1b-3de6-43f7-b917-bd76238af931" "mbid": "e8bdfb1b-3de6-43f7-b917-bd76238af931"
}, },
"Avicii - Without You.mp3": { "Avicii - Without You.mp3": {
"year": null, "year": 2017,
"date": null, "date": "2017",
"title": "Without You", "title": "Without You",
"artist": "Avicii, Sandro Cavazza", "artist": "Avicii, Sandro Cavazza",
"mbid": null "mbid": null
}, },
"Betty Everett - It's In The Kiss (The Shoop Shoop Song).mp3": { "Betty Everett - It's In The Kiss (The Shoop Shoop Song).mp3": {
"year": null, "year": 1963,
"date": null, "date": "1963",
"title": "It's In The Kiss (The Shoop Shoop Song)", "title": "It's In The Kiss (The Shoop Shoop Song)",
"artist": "Betty Everett", "artist": "Betty Everett",
"mbid": null "mbid": null
}, },
"Beyoncé - Crazy In Love (feat. JAY-Z).mp3": { "Beyoncé - Crazy In Love (feat. JAY-Z).mp3": {
"year": null, "year": 2003,
"date": null, "date": "2003",
"title": "Crazy In Love (feat. JAY-Z)", "title": "Crazy In Love (feat. JAY-Z)",
"artist": "Beyoncé, JAY Z", "artist": "Beyoncé, JAY Z",
"mbid": null "mbid": null
@@ -199,8 +199,8 @@
"mbid": "5f92f7de-f6b4-4c52-9f87-e774a268e194" "mbid": "5f92f7de-f6b4-4c52-9f87-e774a268e194"
}, },
"Bob Sinclar - World Hold on (Children of the Sky) [Radio Edit].mp3": { "Bob Sinclar - World Hold on (Children of the Sky) [Radio Edit].mp3": {
"year": 2018, "year": 2006,
"date": "2018-05-04", "date": "2006-04-17",
"title": "World Hold on (Children of the Sky) [Radio Edit]", "title": "World Hold on (Children of the Sky) [Radio Edit]",
"artist": "Bob Sinclar, Steve Edwards", "artist": "Bob Sinclar, Steve Edwards",
"mbid": "9cb36936-6524-4852-ac2d-dd1afac884fa" "mbid": "9cb36936-6524-4852-ac2d-dd1afac884fa"
@@ -227,8 +227,8 @@
"mbid": "a15a76ab-ee46-4212-8b15-86424020f579" "mbid": "a15a76ab-ee46-4212-8b15-86424020f579"
}, },
"Brandy - The Boy Is Mine.mp3": { "Brandy - The Boy Is Mine.mp3": {
"year": null, "year": 1998,
"date": null, "date": "1998",
"title": "The Boy Is Mine", "title": "The Boy Is Mine",
"artist": "Brandy, Monica", "artist": "Brandy, Monica",
"mbid": null "mbid": null
@@ -262,15 +262,15 @@
"mbid": "d5050809-f33a-44bb-8ec1-dc996375e272" "mbid": "d5050809-f33a-44bb-8ec1-dc996375e272"
}, },
"Bryan Ferry - Let's Stick Together (1999 Remastered Version).mp3": { "Bryan Ferry - Let's Stick Together (1999 Remastered Version).mp3": {
"year": null, "year": 1976,
"date": null, "date": "1976",
"title": "Let's Stick Together (1999 Remastered Version)", "title": "Let's Stick Together (1999 Remastered Version)",
"artist": "Bryan Ferry", "artist": "Bryan Ferry",
"mbid": null "mbid": null
}, },
"Buddy Poke - Ab in den Süden.mp3": { "Buddy Poke - Ab in den Süden.mp3": {
"year": null, "year": 2001,
"date": null, "date": "2001",
"title": "Ab in den Süden", "title": "Ab in den Süden",
"artist": "Buddy Poke", "artist": "Buddy Poke",
"mbid": null "mbid": null
@@ -297,8 +297,8 @@
"mbid": "300ec8d8-5053-46ed-92ed-77748648a450" "mbid": "300ec8d8-5053-46ed-92ed-77748648a450"
}, },
"Caterina Valente - Itsy Bitsy Teenie Weenie Honolulu Strand Bikini.mp3": { "Caterina Valente - Itsy Bitsy Teenie Weenie Honolulu Strand Bikini.mp3": {
"year": null, "year": 1986,
"date": null, "date": "1986",
"title": "Itsy Bitsy Teenie Weenie Honolulu Strand Bikini", "title": "Itsy Bitsy Teenie Weenie Honolulu Strand Bikini",
"artist": "Caterina Valente, Silvio Francesco", "artist": "Caterina Valente, Silvio Francesco",
"mbid": null "mbid": null
@@ -318,15 +318,15 @@
"mbid": "c454ddd8-8d0c-419d-b4e9-65620785851e" "mbid": "c454ddd8-8d0c-419d-b4e9-65620785851e"
}, },
"Chic - Le Freak (2018 Remaster).mp3": { "Chic - Le Freak (2018 Remaster).mp3": {
"year": 2019, "year": 1978,
"date": "2019-03-29", "date": "1978",
"title": "Le Freak (2018 Remaster)", "title": "Le Freak (2018 Remaster)",
"artist": "Chic", "artist": "Chic",
"mbid": "b33a9130-63d8-4bf0-a4a2-4bb421ac68e1" "mbid": "b33a9130-63d8-4bf0-a4a2-4bb421ac68e1"
}, },
"Chicago - If You Leave Me Now (Remastered LP Version).mp3": { "Chicago - If You Leave Me Now (Remastered LP Version).mp3": {
"year": 2004, "year": 1976,
"date": "2004-09-15", "date": "1976",
"title": "If You Leave Me Now (Remastered LP Version)", "title": "If You Leave Me Now (Remastered LP Version)",
"artist": "Chicago", "artist": "Chicago",
"mbid": "ef91cc0f-3ebe-4ead-834e-cb34243c3e29" "mbid": "ef91cc0f-3ebe-4ead-834e-cb34243c3e29"
@@ -353,8 +353,8 @@
"mbid": "0a506cdb-0101-4268-9b43-3c64b8498e99" "mbid": "0a506cdb-0101-4268-9b43-3c64b8498e99"
}, },
"Cliff Richard - Congratulations (1998 Remaster).mp3": { "Cliff Richard - Congratulations (1998 Remaster).mp3": {
"year": null, "year": 1972,
"date": null, "date": "1972",
"title": "Congratulations (1998 Remaster)", "title": "Congratulations (1998 Remaster)",
"artist": "Cliff Richard", "artist": "Cliff Richard",
"mbid": null "mbid": null
@@ -374,8 +374,8 @@
"mbid": "e765f8d5-1647-4ead-9547-3d445e06ac5c" "mbid": "e765f8d5-1647-4ead-9547-3d445e06ac5c"
}, },
"Coolio - Gangsta's Paradise.mp3": { "Coolio - Gangsta's Paradise.mp3": {
"year": null, "year": 1995,
"date": null, "date": "1995",
"title": "Gangsta's Paradise", "title": "Gangsta's Paradise",
"artist": "Coolio, L.V.", "artist": "Coolio, L.V.",
"mbid": null "mbid": null
@@ -388,8 +388,8 @@
"mbid": "a2ca096d-b51b-4bd5-bda0-a0875c5f9657" "mbid": "a2ca096d-b51b-4bd5-bda0-a0875c5f9657"
}, },
"Cornelia Froboess - Pack Die Badehose Ein.mp3": { "Cornelia Froboess - Pack Die Badehose Ein.mp3": {
"year": null, "year": 1951,
"date": null, "date": "1951",
"title": "Pack Die Badehose Ein", "title": "Pack Die Badehose Ein",
"artist": "Cornelia Froboess", "artist": "Cornelia Froboess",
"mbid": null "mbid": null
@@ -458,22 +458,22 @@
"mbid": "83e2444b-5445-400c-8215-f12f81d6f615" "mbid": "83e2444b-5445-400c-8215-f12f81d6f615"
}, },
"David Bowie - Space Oddity (1999 Remaster).mp3": { "David Bowie - Space Oddity (1999 Remaster).mp3": {
"year": null, "year": 1969,
"date": null, "date": "1969",
"title": "Space Oddity (1999 Remaster)", "title": "Space Oddity (1999 Remaster)",
"artist": "David Bowie", "artist": "David Bowie",
"mbid": null "mbid": null
}, },
"Dean Martin - That's Amore (2001 Remastered Version).mp3": { "Dean Martin - That's Amore (2001 Remastered Version).mp3": {
"year": null, "year": 1953,
"date": null, "date": "1953",
"title": "That's Amore (2001 Remastered Version)", "title": "That's Amore (2001 Remastered Version)",
"artist": "Dean Martin", "artist": "Dean Martin",
"mbid": null "mbid": null
}, },
"Diana Ross - Upside Down (Single Version).mp3": { "Diana Ross - Upside Down (Single Version).mp3": {
"year": null, "year": 1980,
"date": null, "date": "1980",
"title": "Upside Down (Single Version)", "title": "Upside Down (Single Version)",
"artist": "Diana Ross", "artist": "Diana Ross",
"mbid": null "mbid": null
@@ -493,8 +493,8 @@
"mbid": "8e0d5bf4-0cc0-4ee9-a18c-e6f90f03d737" "mbid": "8e0d5bf4-0cc0-4ee9-a18c-e6f90f03d737"
}, },
"Die Doofen - MIEF! (Nimm mich jetzt, auch wenn ich stinke) Video Version.mp3": { "Die Doofen - MIEF! (Nimm mich jetzt, auch wenn ich stinke) Video Version.mp3": {
"year": null, "year": 1995,
"date": null, "date": "1995",
"title": "MIEF! (Nimm mich jetzt, auch wenn ich stinke) Video Version", "title": "MIEF! (Nimm mich jetzt, auch wenn ich stinke) Video Version",
"artist": "Die Doofen", "artist": "Die Doofen",
"mbid": null "mbid": null
@@ -563,8 +563,8 @@
"mbid": "de4c54c9-6171-4cec-8866-d5f41b24d51d" "mbid": "de4c54c9-6171-4cec-8866-d5f41b24d51d"
}, },
"Dschinghis Khan - Moskau (2007 Version).mp3": { "Dschinghis Khan - Moskau (2007 Version).mp3": {
"year": null, "year": 1979,
"date": null, "date": "1979",
"title": "Moskau (2007 Version)", "title": "Moskau (2007 Version)",
"artist": "Dschinghis Khan", "artist": "Dschinghis Khan",
"mbid": null "mbid": null
@@ -633,15 +633,15 @@
"mbid": "137b53e7-57e3-4a07-85e3-fcf733ae0a0d" "mbid": "137b53e7-57e3-4a07-85e3-fcf733ae0a0d"
}, },
"Enrico Caruso - Rigoletto La Donna E' Mobile.mp3": { "Enrico Caruso - Rigoletto La Donna E' Mobile.mp3": {
"year": null, "year": 1851,
"date": null, "date": "1851",
"title": "Rigoletto: \"La Donna E' Mobile\"", "title": "Rigoletto: \"La Donna E' Mobile\"",
"artist": "Enrico Caruso", "artist": "Enrico Caruso",
"mbid": null "mbid": null
}, },
"Enrique Iglesias - Could I Have This Kiss Forever.mp3": { "Enrique Iglesias - Could I Have This Kiss Forever.mp3": {
"year": null, "year": 1999,
"date": null, "date": "1999",
"title": "Could I Have This Kiss Forever", "title": "Could I Have This Kiss Forever",
"artist": "Enrique Iglesias, Whitney Houston", "artist": "Enrique Iglesias, Whitney Houston",
"mbid": null "mbid": null
@@ -731,15 +731,15 @@
"mbid": "c47d9ac3-b2e3-4305-994d-03211998d650" "mbid": "c47d9ac3-b2e3-4305-994d-03211998d650"
}, },
"Fleetwood Mac - Everywhere (2017 Remaster).mp3": { "Fleetwood Mac - Everywhere (2017 Remaster).mp3": {
"year": null, "year": 1987,
"date": null, "date": "1987",
"title": "Everywhere (2017 Remaster)", "title": "Everywhere (2017 Remaster)",
"artist": "Fleetwood Mac", "artist": "Fleetwood Mac",
"mbid": null "mbid": null
}, },
"Freddy Quinn - Die Gitarre und das Meer (Single Version).mp3": { "Freddy Quinn - Die Gitarre und das Meer (Single Version).mp3": {
"year": null, "year": 1959,
"date": null, "date": "1959",
"title": "Die Gitarre und das Meer (Single Version)", "title": "Die Gitarre und das Meer (Single Version)",
"artist": "Freddy Quinn", "artist": "Freddy Quinn",
"mbid": null "mbid": null
@@ -766,15 +766,15 @@
"mbid": "b4d6e127-5406-42b1-933b-e01a5f7df105" "mbid": "b4d6e127-5406-42b1-933b-e01a5f7df105"
}, },
"George Harrison - Got My Mind Set On You (Remastered 2004).mp3": { "George Harrison - Got My Mind Set On You (Remastered 2004).mp3": {
"year": null, "year": 1962,
"date": null, "date": "1962",
"title": "Got My Mind Set On You (Remastered 2004)", "title": "Got My Mind Set On You (Remastered 2004)",
"artist": "George Harrison", "artist": "George Harrison",
"mbid": null "mbid": null
}, },
"George Michael - I Knew You Were Waiting (For Me).mp3": { "George Michael - I Knew You Were Waiting (For Me).mp3": {
"year": null, "year": 1987,
"date": null, "date": "1987",
"title": "I Knew You Were Waiting (For Me)", "title": "I Knew You Were Waiting (For Me)",
"artist": "George Michael, Aretha Franklin", "artist": "George Michael, Aretha Franklin",
"mbid": null "mbid": null
@@ -815,8 +815,8 @@
"mbid": "95443f12-458b-4977-a01b-c8065bda1dd0" "mbid": "95443f12-458b-4977-a01b-c8065bda1dd0"
}, },
"Haddaway - What Is Love (7 Mix).mp3": { "Haddaway - What Is Love (7 Mix).mp3": {
"year": 1992, "year": 1993,
"date": "1992", "date": "1993-01-18",
"title": "What Is Love (7\" Mix)", "title": "What Is Love (7\" Mix)",
"artist": "Haddaway", "artist": "Haddaway",
"mbid": "2970d12e-5eb9-45c9-8eac-38ce11efc18f" "mbid": "2970d12e-5eb9-45c9-8eac-38ce11efc18f"
@@ -1018,8 +1018,8 @@
"mbid": "cd52b2b1-28d4-4885-9f1d-b44b8caf07f7" "mbid": "cd52b2b1-28d4-4885-9f1d-b44b8caf07f7"
}, },
"Kenny Rogers & Dolly Parton - All I Have To Do Is Dream.mp3": { "Kenny Rogers & Dolly Parton - All I Have To Do Is Dream.mp3": {
"year": null, "year": 1958,
"date": null, "date": "1958",
"title": "All I Have To Do Is Dream", "title": "All I Have To Do Is Dream",
"artist": "Kenny Rogers & Dolly Parton", "artist": "Kenny Rogers & Dolly Parton",
"mbid": null "mbid": null
@@ -1662,29 +1662,29 @@
"mbid": "ab532d7e-1ff0-44cf-a229-301c3506aad6" "mbid": "ab532d7e-1ff0-44cf-a229-301c3506aad6"
}, },
"SDP - Die Nacht von Freitag auf Montag.mp3": { "SDP - Die Nacht von Freitag auf Montag.mp3": {
"year": null, "year": 2012,
"date": null, "date": "2012",
"title": "Die Nacht von Freitag auf Montag", "title": "Die Nacht von Freitag auf Montag",
"artist": "SDP, Sido", "artist": "SDP, Sido",
"mbid": null "mbid": null
}, },
"Shaggy - It Wasn't Me.mp3": { "Shaggy - It Wasn't Me.mp3": {
"year": null, "year": 2000,
"date": null, "date": "2000",
"title": "It Wasn't Me", "title": "It Wasn't Me",
"artist": "Shaggy, Ricardo Ducent", "artist": "Shaggy, Ricardo Ducent",
"mbid": null "mbid": null
}, },
"Shakira - Waka Waka (This Time for Africa) [The Official 2010 FIFA World Cup (TM) Song] (feat. Freshlyground) (Single).mp3": { "Shakira - Waka Waka (This Time for Africa) [The Official 2010 FIFA World Cup (TM) Song] (feat. Freshlyground) (Single).mp3": {
"year": null, "year": 2010,
"date": null, "date": "2010",
"title": "Waka Waka (This Time for Africa) [The Official 2010 FIFA World Cup (TM) Song] (feat. Freshlyground) (Single)", "title": "Waka Waka (This Time for Africa) [The Official 2010 FIFA World Cup (TM) Song] (feat. Freshlyground) (Single)",
"artist": "Shakira, Freshlyground", "artist": "Shakira, Freshlyground",
"mbid": null "mbid": null
}, },
"Shawn Mendes - Señorita.mp3": { "Shawn Mendes - Señorita.mp3": {
"year": null, "year": 2019,
"date": null, "date": "2019-06-21",
"title": "Señorita", "title": "Señorita",
"artist": "Shawn Mendes, Camila Cabello", "artist": "Shawn Mendes, Camila Cabello",
"mbid": null "mbid": null
@@ -1704,8 +1704,8 @@
"mbid": "1fb661ee-5789-4ec3-a372-55417788a089" "mbid": "1fb661ee-5789-4ec3-a372-55417788a089"
}, },
"Simply Red - Holding Back the Years (2008 Remaster).mp3": { "Simply Red - Holding Back the Years (2008 Remaster).mp3": {
"year": null, "year": 1985,
"date": null, "date": "1985",
"title": "Holding Back the Years (2008 Remaster)", "title": "Holding Back the Years (2008 Remaster)",
"artist": "Simply Red", "artist": "Simply Red",
"mbid": null "mbid": null
@@ -1718,8 +1718,8 @@
"mbid": "cfa03e25-2700-490c-b087-9bc8feec5cb5" "mbid": "cfa03e25-2700-490c-b087-9bc8feec5cb5"
}, },
"Sister Sledge - We Are Family (1995 Remaster).mp3": { "Sister Sledge - We Are Family (1995 Remaster).mp3": {
"year": 2018, "year": 1979,
"date": "2018-10-12", "date": "1979-01-22",
"title": "We Are Family (1995 Remaster)", "title": "We Are Family (1995 Remaster)",
"artist": "Sister Sledge", "artist": "Sister Sledge",
"mbid": "94f14692-3dc9-458f-a9ae-4bac3acff1b4" "mbid": "94f14692-3dc9-458f-a9ae-4bac3acff1b4"
@@ -1795,8 +1795,8 @@
"mbid": "7134eba5-f912-426c-a44d-7c19d9d4706c" "mbid": "7134eba5-f912-426c-a44d-7c19d9d4706c"
}, },
"Taio Cruz - Hangover.mp3": { "Taio Cruz - Hangover.mp3": {
"year": null, "year": 2011,
"date": null, "date": "2011",
"title": "Hangover", "title": "Hangover",
"artist": "Taio Cruz, Flo Rida", "artist": "Taio Cruz, Flo Rida",
"mbid": null "mbid": null
@@ -1823,8 +1823,8 @@
"mbid": "fa52f01e-6c8c-46ff-860d-daa4930f93a4" "mbid": "fa52f01e-6c8c-46ff-860d-daa4930f93a4"
}, },
"The Beatles - Get Back (Remastered 2009).mp3": { "The Beatles - Get Back (Remastered 2009).mp3": {
"year": null, "year": 2021,
"date": null, "date": "2021",
"title": "Get Back (Remastered 2009)", "title": "Get Back (Remastered 2009)",
"artist": "The Beatles", "artist": "The Beatles",
"mbid": null "mbid": null
@@ -1893,8 +1893,8 @@
"mbid": "1bb8b072-d300-4940-be9c-28800f15a29a" "mbid": "1bb8b072-d300-4940-be9c-28800f15a29a"
}, },
"The Police - Every Breath You Take (Remastered 2003).mp3": { "The Police - Every Breath You Take (Remastered 2003).mp3": {
"year": 2023, "year": 1983,
"date": "2023-08-11", "date": "1983",
"title": "Every Breath You Take (Remastered 2003)", "title": "Every Breath You Take (Remastered 2003)",
"artist": "The Police", "artist": "The Police",
"mbid": "e60981c2-c79a-4687-ac26-7ad730fe34e6" "mbid": "e60981c2-c79a-4687-ac26-7ad730fe34e6"
@@ -1928,8 +1928,8 @@
"mbid": "fe1bbf77-90a5-4d5d-9a7e-b002966510c8" "mbid": "fe1bbf77-90a5-4d5d-9a7e-b002966510c8"
}, },
"The Verve - Bitter Sweet Symphony (Remastered 2016).mp3": { "The Verve - Bitter Sweet Symphony (Remastered 2016).mp3": {
"year": null, "year": 1997,
"date": null, "date": "1997",
"title": "Bitter Sweet Symphony (Remastered 2016)", "title": "Bitter Sweet Symphony (Remastered 2016)",
"artist": "The Verve", "artist": "The Verve",
"mbid": null "mbid": null
@@ -1970,8 +1970,8 @@
"mbid": "c8ac6945-bd95-4ddc-b7f7-bc2237db0ef7" "mbid": "c8ac6945-bd95-4ddc-b7f7-bc2237db0ef7"
}, },
"Tokio Hotel - Durch den Monsun (Radio Mix).mp3": { "Tokio Hotel - Durch den Monsun (Radio Mix).mp3": {
"year": null, "year": 2005,
"date": null, "date": "2005",
"title": "Durch den Monsun (Radio Mix)", "title": "Durch den Monsun (Radio Mix)",
"artist": "Tokio Hotel", "artist": "Tokio Hotel",
"mbid": null "mbid": null
@@ -2593,8 +2593,8 @@
"title": "It's My Life", "title": "It's My Life",
"artist": "Bon Jovi", "artist": "Bon Jovi",
"mbid": "511096e6-7737-4514-8f23-bfa0dac765ec", "mbid": "511096e6-7737-4514-8f23-bfa0dac765ec",
"earliestDate": "1993", "earliestDate": "2000",
"year": 1993, "year": 2000,
"confidence": { "confidence": {
"mbScore": 100, "mbScore": 100,
"titleSim": 1, "titleSim": 1,
@@ -3109,8 +3109,8 @@
"title": "Upside Down (Single Version)", "title": "Upside Down (Single Version)",
"artist": "Diana Ross", "artist": "Diana Ross",
"mbid": null, "mbid": null,
"earliestDate": null, "earliestDate": 1980,
"year": null, "year": 1980,
"error": "No recordings found" "error": "No recordings found"
}, },
{ {
@@ -3484,8 +3484,8 @@
"title": "Hungry Eyes (From \"Dirty Dancing\" Soundtrack)", "title": "Hungry Eyes (From \"Dirty Dancing\" Soundtrack)",
"artist": "Eric Carmen", "artist": "Eric Carmen",
"mbid": "848ecccf-4fc4-4478-b70a-8e31286bfd84", "mbid": "848ecccf-4fc4-4478-b70a-8e31286bfd84",
"earliestDate": "2005", "earliestDate": "1987",
"year": 2005, "year": 1987,
"confidence": { "confidence": {
"mbScore": 100, "mbScore": 100,
"titleSim": 1, "titleSim": 1,
@@ -3652,8 +3652,8 @@
"title": "Heimweh (Dort wo die Blumen blüh'n)", "title": "Heimweh (Dort wo die Blumen blüh'n)",
"artist": "Freddy Quinn", "artist": "Freddy Quinn",
"mbid": "2e4c3d92-9103-48ee-9399-43d83a61872f", "mbid": "2e4c3d92-9103-48ee-9399-43d83a61872f",
"earliestDate": "2006-10-26", "earliestDate": "1987-10-26",
"year": 2006, "year": 1987,
"confidence": { "confidence": {
"mbScore": 100, "mbScore": 100,
"titleSim": 0.875, "titleSim": 0.875,
@@ -3697,8 +3697,8 @@
"title": "Got My Mind Set On You (Remastered 2004)", "title": "Got My Mind Set On You (Remastered 2004)",
"artist": "George Harrison", "artist": "George Harrison",
"mbid": null, "mbid": null,
"earliestDate": null, "earliestDate": "1987",
"year": null, "year": 1987,
"error": "No recordings found" "error": "No recordings found"
}, },
{ {
@@ -3730,8 +3730,8 @@
"title": "Ich will 'nen Cowboy als Mann", "title": "Ich will 'nen Cowboy als Mann",
"artist": "Gitte Hænning", "artist": "Gitte Hænning",
"mbid": "37e33915-b7e5-4310-bc53-94d17310b26c", "mbid": "37e33915-b7e5-4310-bc53-94d17310b26c",
"earliestDate": "2003", "earliestDate": "1963",
"year": 2003, "year": 1963,
"confidence": { "confidence": {
"mbScore": 100, "mbScore": 100,
"titleSim": 1, "titleSim": 1,

View File

@@ -46,7 +46,8 @@ let state = {
const el = (id) => document.getElementById(id); const el = (id) => document.getElementById(id);
const $lobby = el('lobby'); const $lobby = el('lobby');
const $room = el('room'); const $room = el('room');
const $players = el('players'); // Removed old players chip list; using dashboard only
const $dashboardList = el('dashboardList');
const $roomId = el('roomId'); const $roomId = el('roomId');
const $status = el('status'); const $status = el('status');
const $guesser = el('guesser'); const $guesser = el('guesser');
@@ -129,22 +130,38 @@ function showToast(msg) {
// Show my current name (from server if available) or fallback to stored value // Show my current name (from server if available) or fallback to stored value
const me = room.players.find(p=>p.id===state.playerId); const me = room.players.find(p=>p.id===state.playerId);
if ($nameDisplay) $nameDisplay.textContent = (me?.name || localStorage.getItem('playerName') || '-'); if ($nameDisplay) $nameDisplay.textContent = (me?.name || localStorage.getItem('playerName') || '-');
$players.innerHTML = room.players.map(p => { // Old players chip list removed
const badges = [ // Dashboard rows
p.id===room.hostId ? '<span class="ml-1 text-amber-600">⭐</span>' : '', if ($dashboardList) {
p.ready ? '<span class="ml-1 text-emerald-600">✓</span>' : '', $dashboardList.innerHTML = room.players.map(p => {
!p.connected ? '<span class="ml-1 text-rose-600">(off)</span>' : '', const connected = p.connected ? '<span class="text-emerald-600">online</span>' : '<span class="text-rose-600">offline</span>';
].join(''); const ready = p.ready ? '<span class="text-emerald-600">bereit</span>' : '<span class="text-slate-400">-</span>';
return `<span class="inline-flex items-center gap-1 px-2 py-1 rounded-full border border-slate-300 dark:border-slate-700 text-sm">${escapeHtml(p.name)}${badges}</span>`; const score = (room.state.timeline?.[p.id]?.length) ?? 0;
const isMe = p.id === state.playerId;
return `
<tr class="align-top">
<td class="py-2 pr-3">
<div class="inline-flex items-center gap-1">
<span>${escapeHtml(p.name)}</span>${p.spectator ? ' <span title="Zuschauer">👻</span>' : ''}
${p.id===room.hostId ? '<span title="Host" class="text-amber-600">\u2B50</span>' : ''}
${isMe ? '<span title="Du" class="text-indigo-600">(du)</span>' : ''}
</div>
</td>
<td class="py-2 pr-3">${connected}</td>
<td class="py-2 pr-3">${ready}</td>
<td class="py-2 pr-3 font-semibold tabular-nums">${score}</td>
</tr>`;
}).join(''); }).join('');
}
const myTl = room.state.timeline?.[state.playerId] || []; const myTl = room.state.timeline?.[state.playerId] || [];
$timeline.innerHTML = myTl.map(t => { $timeline.innerHTML = myTl.map(t => {
const title = escapeHtml(t.title || t.trackId || 'Unbekannt'); const title = escapeHtml(t.title || t.trackId || 'Unbekannt');
const artist = t.artist ? escapeHtml(t.artist) : ''; const artist = t.artist ? escapeHtml(t.artist) : '';
const year = (t.year ?? '?'); const year = (t.year ?? '?');
const badgeStyle = badgeColorForYear(year);
return ` return `
<div class="flex items-center gap-2 border border-slate-200 dark:border-slate-800 rounded-lg px-3 py-2 bg-white text-slate-900 dark:bg-slate-800 dark:text-slate-100 shadow-sm" title="${title}${artist? ' — '+artist : ''} (${year})"> <div class="flex items-center gap-2 border border-slate-200 dark:border-slate-800 rounded-lg px-3 py-2 bg-white text-slate-900 dark:bg-slate-800 dark:text-slate-100 shadow-sm" title="${title}${artist? ' — '+artist : ''} (${year})">
<div class="font-bold tabular-nums bg-indigo-600 text-white rounded-md px-2 py-0.5 min-w-[3ch] text-center">${year}</div> <div class="font-bold tabular-nums text-white rounded-md px-2 py-0.5 min-w-[3ch] text-center" style="${badgeStyle}">${year}</div>
<div class="leading-tight"> <div class="leading-tight">
<div class="font-semibold">${title}</div> <div class="font-semibold">${title}</div>
<div class="text-sm text-slate-600 dark:text-slate-300">${artist}</div> <div class="text-sm text-slate-600 dark:text-slate-300">${artist}</div>
@@ -216,6 +233,18 @@ function shortName(id) {
function escapeHtml(s) { return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); } function escapeHtml(s) { return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }
// Stable distinct color per year for the year badge
function badgeColorForYear(y) {
const val = (y === undefined || y === null) ? '?' : y;
if (val === '?' || Number.isNaN(Number(val))) {
// Neutral slate for unknown years
return 'background-color: hsl(215 16% 34%);';
}
const n = Number(val);
const hue = ((n * 23) % 360 + 360) % 360; // spread hues deterministically
return `background-color: hsl(${hue} 70% 42%);`;
}
function handleMessage(ev) { function handleMessage(ev) {
const msg = JSON.parse(ev.data); const msg = JSON.parse(ev.data);
if (msg.type === 'connected') { if (msg.type === 'connected') {

View File

@@ -46,9 +46,29 @@
<div class="text-slate-700 dark:text-slate-300">Dein Name: <strong id="nameDisplay" class="font-semibold"></strong></div> <div class="text-slate-700 dark:text-slate-300">Dein Name: <strong id="nameDisplay" class="font-semibold"></strong></div>
<!-- Expandable Dashboard: player statuses and scores -->
<div> <div>
<h3 class="text-sm font-semibold text-slate-500 uppercase tracking-wide">Spieler</h3> <details id="dashboard" class="rounded-lg border border-slate-200 dark:border-slate-800 bg-white/60 dark:bg-slate-900/40 p-3">
<div id="players" class="mt-2 flex flex-wrap gap-2"></div> <summary class="cursor-pointer select-none text-sm font-semibold text-slate-700 dark:text-slate-300 flex items-center gap-2">
<span class="inline-flex h-5 w-5 items-center justify-center rounded-full bg-indigo-600 text-white text-[11px] font-bold">i</span>
Dashboard: Spielerstatus & Punkte
</summary>
<div class="mt-3 overflow-x-auto">
<table class="min-w-full text-sm">
<thead class="text-left text-slate-500 dark:text-slate-400">
<tr>
<th class="py-2 pr-3">Spieler</th>
<th class="py-2 pr-3">Verbindung</th>
<th class="py-2 pr-3">Ready</th>
<th class="py-2 pr-3">Score</th>
</tr>
</thead>
<tbody id="dashboardList" class="divide-y divide-slate-200 dark:divide-slate-800"></tbody>
</table>
</div>
</details>
</div> </div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 items-start"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 items-start">

View File

@@ -156,6 +156,7 @@ function createRoom(name, host) {
timeline: {}, // playerId -> [{trackId, year, title, artist}] timeline: {}, // playerId -> [{trackId, year, title, artist}]
tokens: {}, // playerId -> number tokens: {}, // playerId -> number
ready: { [host.id]: false }, // playerId -> boolean ready: { [host.id]: false }, // playerId -> boolean
spectators: {}, // playerId -> boolean (joined after game start)
phase: 'guess', // 'guess' | 'reveal' phase: 'guess', // 'guess' | 'reveal'
lastResult: null, // { playerId, correct } lastResult: null, // { playerId, correct }
trackStartAt: null, // ms epoch for synced start time trackStartAt: null, // ms epoch for synced start time
@@ -186,6 +187,7 @@ function roomSummary(room) {
name: p.name, name: p.name,
connected: p.connected, connected: p.connected,
ready: !!room.state.ready?.[p.id], ready: !!room.state.ready?.[p.id],
spectator: !!room.state.spectators?.[p.id] || !!p.spectator,
})), })),
state: room.state, state: room.state,
}; };
@@ -331,6 +333,14 @@ wss.on('connection', (ws) => {
room.players.set(player.id, player); room.players.set(player.id, player);
player.roomId = room.id; player.roomId = room.id;
room.state.ready[player.id] = false; room.state.ready[player.id] = false;
// If the game already started, mark as spectator
if (room.state.status === 'playing' || room.state.status === 'ended') {
room.state.spectators[player.id] = true;
player.spectator = true;
} else {
delete room.state.spectators[player.id];
player.spectator = false;
}
broadcast(room, 'room_update', { room: roomSummary(room) }); broadcast(room, 'room_update', { room: roomSummary(room) });
return; return;
} }
@@ -342,6 +352,7 @@ wss.on('connection', (ws) => {
room.players.delete(player.id); room.players.delete(player.id);
player.roomId = null; player.roomId = null;
if (room.state.ready) delete room.state.ready[player.id]; if (room.state.ready) delete room.state.ready[player.id];
if (room.state.spectators) delete room.state.spectators[player.id];
if (room.players.size === 0) rooms.delete(room.id); if (room.players.size === 0) rooms.delete(room.id);
else broadcast(room, 'room_update', { room: roomSummary(room) }); else broadcast(room, 'room_update', { room: roomSummary(room) });
return; return;
@@ -361,7 +372,9 @@ wss.on('connection', (ws) => {
if (!room) return; if (!room) return;
if (room.hostId !== player.id) return send('error', { message: 'Only host can start' }); if (room.hostId !== player.id) return send('error', { message: 'Only host can start' });
// All players must be ready // All players must be ready
const pids = [...room.players.keys()]; const pids = [...room.players.values()]
.filter(p => !room.state.spectators?.[p.id])
.map(p => p.id);
const allReady = pids.every((pid) => !!room.state.ready?.[pid]); const allReady = pids.every((pid) => !!room.state.ready?.[pid]);
if (!allReady) return send('error', { message: 'All players must be ready' }); if (!allReady) return send('error', { message: 'All players must be ready' });
room.state.status = 'playing'; room.state.status = 'playing';