final touches to interface to include album covers, and more

This commit is contained in:
suroh 2023-10-11 18:06:52 +02:00
parent 1fedcdf92b
commit a1b0439bd0
25 changed files with 1840 additions and 668 deletions

View File

@ -15,6 +15,7 @@
"urlpattern-polyfill": "^9.0.0" "urlpattern-polyfill": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"chokidar": "^3.5.3",
"eslint": "^8.49.0", "eslint": "^8.49.0",
"eslint-plugin-lit": "^1.9.1", "eslint-plugin-lit": "^1.9.1",
"sass": "^1.67.0", "sass": "^1.67.0",

View File

@ -1,50 +1,51 @@
[ [
{ {
"title": "Late Junction", "title": "Fireside Chat",
"details": "BBC, 2019", "details": "Mark \"Frosty\" McNeil, Red Bull Radio, 2019",
"parent_dir": "documentaries_interviews_audio", "parent_dir": "documentaries_interviews_audio",
"media": "-21 08_Late Junction_2019 - Meredith Monk v2 BBC" "media": "Fireside Chat_Mark \"Frosty\" McNeil, Red Bull Radio, 2019.mp3",
"image": "Fireside Chat_Mark \"Frosty\" McNeil, Red Bull Radio, 2019.png"
}, },
{ {
"title": "Speaking of Music", "title": "Irene Ferchl, Bayerischer Rundfunk",
"details": "Meredith Monk, 1984 Part 1 of 2", "details": "Kulturjournal, 1986",
"parent_dir": "documentaries_interviews_audio", "parent_dir": "documentaries_interviews_audio",
"media": "-22 02_Speaking of Music Meredith Monk 1984 Part 1 of 2" "media": "Irene Ferchl, Bayerischer Rundfunk_Kulturjournal, 1986.mp3",
}, "image": "Irene Ferchl, Bayerischer Rundfunk_Kulturjournal, 1986.png"
{
"title": "Speaking of Music",
"details": "Meredith Monk, 1984 Part 2 of 2",
"parent_dir": "documentaries_interviews_audio",
"media": "-23 03_Speaking of Music_ Meredith Monk 1984 Part 2 of 2"
},
{
"title": "Interview with I Frechl-Bayn Rundfunk",
"details": "1986",
"parent_dir": "documentaries_interviews_audio",
"media": "-24 04 Meredith Monk Interview with I Ferchl_Bayn Rundfunk 1986"
},
{
"title": "Terry Gross Interview",
"details": "1987",
"parent_dir": "documentaries_interviews_audio",
"media": "-25 05 Terry Gross interview_1987"
},
{
"title": "Interview with David Garland",
"details": "WNYC, Sacred Sundays, 2008",
"parent_dir": "documentaries_interviews_audio",
"media": "-26 06 WNYC Interview w_ David Garland_SacredSundays_2008"
},
{
"title": "Frosty McNeil and Meredith Monk",
"details": "Fireside Chat, Red Bull Radio",
"parent_dir": "documentaries_interviews_audio",
"media": "-27 07_Red Bull Radio_Fireside Chat_M frosty McNeil and MM_41119"
}, },
{ {
"title": "Late Junction", "title": "Late Junction",
"details": "BBC, 2019", "details": "Jennifer Lucy Allan, BBC Radio 3, 2019",
"parent_dir": "documentaries_interviews_audio", "parent_dir": "documentaries_interviews_audio",
"media": "-28 08_Late Junction_2019 - Meredith Monk v2 BBC" "media": "Late Junction_Jennifer Lucy Allan, BBC Radio 3, 2019.mp3",
"image": "Late Junction_Jennifer Lucy Allan, BBC Radio 3, 2019.png"
},
{
"title": "Sacred Sundays",
"details": "David Garland, WNYC, 2008",
"parent_dir": "documentaries_interviews_audio",
"media": "Sacred Sundays_David Garland, WNYC, 2008.mp3",
"image": "Sacred Sundays_David Garland, WNYC, 2008.png"
},
{
"title": "Speaking of Music",
"details": "Charles Amirkhanian, Other Minds, 1984. Part 1 of 2",
"parent_dir": "documentaries_interviews_audio",
"media": "Speaking of Music_Charles Amirkhanian, Other Minds, 1984. Part 1 of 2..mp3",
"image": "Speaking of Music_Charles Amirkhanian, Other Minds, 1984. Part 1 of 2..png"
},
{
"title": "Speaking of Music",
"details": "Charles Amirkhanian, Other Minds, 1984. Part 2 of 2",
"parent_dir": "documentaries_interviews_audio",
"media": "Speaking of Music_Charles Amirkhanian, Other Minds, 1984. Part 2 of 2..mp3",
"image": "Speaking of Music_Charles Amirkhanian, Other Minds, 1984. Part 2 of 2..png"
},
{
"title": "Terry Gross",
"details": "unedited, 1987",
"parent_dir": "documentaries_interviews_audio",
"media": "Terry Gross_unedited, 1987.mp3",
"image": "Terry Gross_unedited, 1987.png"
} }
] ]

View File

@ -1,92 +1,114 @@
[ [
{ {
"title": "Michael Blackwood Making Dances", "title": "4 American Composers",
"details": "", "details": "documentary by Peter Greenaway, 1983",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "--2 MichaelBlackwoodMakingDances.m4v" "media": "4 American Composers_documentary by Peter Greenaway, 1983.m4v",
}, "image": "4 American Composers_documentary by Peter Greenaway, 1983.jpg"
{
"title": "ECM 1981",
"details": "2019",
"parent_dir": "documentaries_interviews_video",
"media": "--3 ECM1981 Video Meredith Monk V2_2019.m4v"
}, },
{ {
"title": "Andere Avantgarde", "title": "Andere Avantgarde",
"details": "TV Interview 1982", "details": "television documentary by Peter Greenaway, 1983",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "--4 Meredith Monk_Andere Avantgarde_TV Interview_1982_.m4v" "media": "Andere Avantgarde_television documentary by Peter Greenaway, 1983.m4v",
}, "image": "Andere Avantgarde_television documentary by Peter Greenaway, 1983.jpg"
{
"title": "New Sounds for a New World",
"details": "1982",
"parent_dir": "documentaries_interviews_video",
"media": "--5 New Sounds for a New World_1982.m4v"
},
{
"title": "Four American Composers",
"details": "PeterGreenaway",
"parent_dir": "documentaries_interviews_video",
"media": "--6 Four American Composers_Peter Greenaway.m4v"
}, },
{ {
"title": "BBC Late Show", "title": "BBC Late Show",
"details": "", "details": "television interview, 1991",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "--7 bbc_late_show (Original).m4v" "media": "BBC Late Show_television interview, 1991.m4v",
"image": "BBC Late Show_television interview, 1991.jpg"
}, },
{ {
"title": "Documentary by Sidsel Mundal", "title": "Coffee and Composition",
"details": "Norwegian Television, 1994", "details": "Meredith Monk and Robert Kyr, online, 2021",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "--8 1994 documentary by Sidsel Mundal_for Norwegian Telev.m4v" "media": "Coffee and Composition_Meredith Monk and Robert Kyr, online, 2021.m4v",
"image": "Coffee and Composition_Meredith Monk and Robert Kyr, online, 2021.jpg"
}, },
{ {
"title": "Television Documentary", "title": "Dorothy and Lillian Gish Award",
"details": "Polish Television, 1995", "details": "portrait interview with Meredith Monk, 2017",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "--9 Meredith Monk - Polish Television Documentary (1995).m4v" "media": "Dorothy and Lillian Gish Award_portrait interview with Meredith Monk, 2017.m4v",
"image": "Dorothy and Lillian Gish Award_portrait interview with Meredith Monk, 2017.jpg"
}, },
{ {
"title": "Inner Voice", "title": "ECM 50 | 1981 Meredith Monk",
"details": "", "details": "documentary by Ingo Berhman for ECM Records' 50th Anniversary, 2019",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "-12 MeredithMonk_Inner Voice.m4v" "media": "ECM 50 | 1981 Meredith Monk_documentary by Ingo Berhman for ECM Records' 50th Anniversary, 2019.m4v",
"image": "ECM 50 | 1981 Meredith Monk_documentary by Ingo Berhman for ECM Records' 50th Anniversary, 2019.jpg"
}, },
{ {
"title": "Lecutre Performance", "title": "Girlchild Diary",
"details": "Bennington College, 11 March 2011", "details": "documentary on Education of the Girlchild, ed. Joëlle Schon, 2015",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "-13 Bennington College lecture performance 3.11.11.m4v" "media": "Girlchild Diary_documentary on Education of the Girlchild, ed. Joëlle Schon, 2015.m4v",
"image": "Girlchild Diary_documentary on Education of the Girlchild, ed. Joëlle Schon, 2015.jpg"
}, },
{ {
"title": "", "title": "MMonk",
"details": "TheSoulsMessenger",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "-14 MMonk_TheSoulsMessenger_Lublin_2012.m4v" "media": "MMonk_TheSoulsMessenger_Lublin_2012.m4v",
"image": "MMonk_TheSoulsMessenger_Lublin_2012.jpg"
}, },
{ {
"title": "", "title": "Making Dances",
"details": "documentary by Michael Blackwood, 1980",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "-15 Girlchild Diary_61116.m4v" "media": "Making Dances_documentary by Michael Blackwood, 1980.m4v",
"image": "Making Dances_documentary by Michael Blackwood, 1980.jpg"
}, },
{ {
"title": "", "title": "Meredith Monk and Adam Shatz",
"details": "conversation on Book of Days, online, 2020",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "-16 Q2 Spaces_Meredith Monk_2014.m4v" "media": "Meredith Monk and Adam Shatz_conversation on Book of Days, online, 2020.m4v",
"image": "Meredith Monk and Adam Shatz_conversation on Book of Days, online, 2020.jpg"
}, },
{ {
"title": "", "title": "Meredith Monk lecture",
"details": "Bennington College, Vermont, 2011",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "-17 Gish Prize Ceremony Camera A.m4v" "media": "Meredith Monk lecture_Bennington College, Vermont, 2011.m4v",
"image": "Meredith Monk lecture_Bennington College, Vermont, 2011.jpg"
}, },
{ {
"title": "", "title": "Meredith Monk: Inner Voice",
"details": "documentary by Babeth M. VanLoo, Buddhist Broadcasting Foundation, 2009",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "-18 MM and Adam Shatz - Book of Days conversation_2020.m4v" "media": "Meredith Monk: Inner Voice_documentary by Babeth M. VanLoo, Buddhist Broadcasting Foundation, 2009.m4v",
"image": "Meredith Monk: Inner Voice_documentary by Babeth M. VanLoo, Buddhist Broadcasting Foundation, 2009.jpg"
}, },
{ {
"title": "", "title": "Meredith Monk",
"details": "documentary by Sidsel Mundal for Norwegian Television, 1994",
"parent_dir": "documentaries_interviews_video", "parent_dir": "documentaries_interviews_video",
"media": "-19 Coffee and Composition with Meredith Monk_2021.m4v" "media": "Meredith Monk_documentary by Sidsel Mundal for Norwegian Television, 1994.m4v",
"image": "Meredith Monk_documentary by Sidsel Mundal for Norwegian Television, 1994.jpg"
},
{
"title": "Meredith",
"details": "documentary by Mariusz Grzegorzeki for Polish Television, 1995",
"parent_dir": "documentaries_interviews_video",
"media": "Meredith_documentary by Mariusz Grzegorzeki for Polish Television, 1995.m4v",
"image": "Meredith_documentary by Mariusz Grzegorzeki for Polish Television, 1995.jpg"
},
{
"title": "Neue Töne aus der Neuen Welt",
"details": "(New Sounds for a New World) documentary by Birgitta Ashoff for German Telelvision (in German), 1983",
"parent_dir": "documentaries_interviews_video",
"media": "Neue Töne aus der Neuen Welt_(New Sounds for a New World) documentary by Birgitta Ashoff for German Telelvision (in German), 1983.m4v",
"image": "Neue Töne aus der Neuen Welt_(New Sounds for a New World) documentary by Birgitta Ashoff for German Telelvision (in German), 1983.jpg"
},
{
"title": "Q2 Spaces: Meredith Monk",
"details": "documentary, 2014",
"parent_dir": "documentaries_interviews_video",
"media": "Q2 Spaces: Meredith Monk_documentary, 2014.m4v",
"image": "Q2 Spaces: Meredith Monk_documentary, 2014.jpg"
} }
] ]

View File

@ -1,27 +1,44 @@
[ [
{ {
"title": "", "title": "16 Millimeter Earrings",
"parent_dir": "films", "details": "Robert S. Withers, 1979",
"media": "_72 01 16_Millimeter_Earrings_1980_Color_IN_HD_23-98_ProRes4444_Pillar Scan GOOD copy v4 2023 0812.m4v" "parent_dir": "Films",
"media": "16 Millimeter Earrings_Robert S. Withers, 1979.m4v",
"image": "16 Millimeter Earrings_Robert S. Withers, 1979.jpg"
}, },
{ {
"title": "", "title": "Book of Days",
"parent_dir": "films", "details": "Meredith Monk, 1988",
"media": "_74 Ellis Island.m4v" "parent_dir": "Films",
"media": "Book of Days_Meredith Monk, 1988.m4v",
"image": "Book of Days_Meredith Monk, 1988.jpg"
}, },
{ {
"title": "", "title": "Ellis Island",
"parent_dir": "films", "details": "Meredith Monk and Bob Rosen, 1981",
"media": "_75 Book of Days.m4v" "parent_dir": "Films",
"media": "Ellis Island_Meredith Monk and Bob Rosen, 1981.m4v",
"image": "Ellis Island_Meredith Monk and Bob Rosen, 1981.jpg"
}, },
{ {
"title": "", "title": "Paris",
"parent_dir": "films", "details": "Meredith Monk and Ping Chong, 1982",
"media": "_76 1982 Paris KTCA (full).m4v" "parent_dir": "Films",
"media": "Paris_Meredith Monk and Ping Chong, 1982.m4v",
"image": "Paris_Meredith Monk and Ping Chong, 1982.jpg"
}, },
{ {
"title": "", "title": "Quarry",
"parent_dir": "films", "details": "1977",
"media": "_77 TurtleDreams(Waltz)_Stereo_Tranfer1inchApril2018.m4v" "parent_dir": "Films",
"media": "Quarry_1977.m4v",
"image": "Quarry_1977.jpg"
},
{
"title": "Turtle Dreams (Waltz)",
"details": "Meredith Monk, 1983",
"parent_dir": "Films",
"media": "Turtle Dreams (Waltz)_Meredith Monk, 1983.m4v",
"image": "Turtle Dreams (Waltz)_Meredith Monk, 1983.jpg"
} }
] ]

View File

@ -1,18 +1 @@
[ []
{
"title": "Photo One",
"media": "photo_one.jpg"
},
{
"title": "Photo Two",
"media": "photo_two.jpg"
},
{
"title": "Photo Three",
"media": "photo_three.jpg"
},
{
"title": "Photo Four",
"media": "photo_four.jpg"
}
]

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1 @@
[ []
{
"title": "",
"parent_dir": "music_sound_unpublished",
"media": "no content recieved",
"tracks": []
}
]

View File

@ -1,184 +1,510 @@
[ [
{ {
"title": "", "title": "Beginnings",
"details": "Tzadik, 2009",
"parent_dir": "music_sound_various", "parent_dir": "music_sound_various",
"media": "MONK MIX Remixes and Interpretations of Music by Meredith Monk CD1", "media": "Beginnings_Tzadik, 2009",
"image": "Beginnings_Tzadik, 2009.jpg",
"album": true, "album": true,
"tracks": [ "tracks": [
"01 Gotham Lullaby (Featuring Björk With The Brodsky Quartet).mp3", {
"02 Caldera Chimera (Featuring Gabriel Prokofiev Remix).mp3", "title": "_60_01 Greensleeves",
"03 Click Song #1 (Featuring Don Byron).mp3", "media": "_60_01 Greensleeves.mp3"
"04 Double Fiesta (Featuring Meredith Monk & Bang On A Can).mp3", },
"05 Astronaut Anthem (Featuring Sakamoto Remix).mp3", {
"06 Shaking (Featuring Lukas Ligeti) [Pyrolator Remix].mp3", "title": "_60_02 Nota",
"07 Last Song (Featuring Caetano Veloso).mp3", "media": "_60_02 Nota.mp3"
"08 Fat Stream (Featuring Nico_s Piano Homage).mp3", },
"09 Wheel (Featuring John Hollenbeck & Theo Bleckmann).mp3", {
"10 Scared Song (Featuring Pamela Z).mp3", "title": "_60_03 Duet For Voice And Echoplex",
"11 Boat Song (Featuring Rubin Kodheli).mp3", "media": "_60_03 Duet For Voice And Echoplex.mp3"
"12 Gathering (Featuring Lee Ranaldo Remix).mp3", },
"13 Evening (Featuring Henry Grimes Remix).mp3" {
"title": "_60_04 Candy Bullets And Moon",
"media": "_60_04 Candy Bullets And Moon.mp3"
},
{
"title": "_60_05 Trance",
"media": "_60_05 Trance.mp3"
},
{
"title": "_60_06 Epic I",
"media": "_60_06 Epic I.mp3"
},
{
"title": "_60_07 Paris",
"media": "_60_07 Paris.mp3"
},
{
"title": "_60_08 Biography",
"media": "_60_08 Biography.mp3"
},
{
"title": "_60_09 Mill",
"media": "_60_09 Mill.mp3"
},
{
"title": "_60_10 The Tale",
"media": "_60_10 The Tale.mp3"
},
{
"title": "_60_11 Quarry Weave",
"media": "_60_11 Quarry Weave.mp3"
},
{
"title": "_60_12 Epic II",
"media": "_60_12 Epic II.mp3"
},
{
"title": "_60_13 Tower",
"media": "_60_13 Tower.mp3"
},
{
"title": "_60_14 Mill",
"media": "_60_14 Mill.mp3"
},
{
"title": "_60_15 Do You Be_",
"media": "_60_15 Do You Be_.mp3"
},
{
"title": "_60_16 Quarry Procession",
"media": "_60_16 Quarry Procession.mp3"
},
{
"title": "_60_17 Porch",
"media": "_60_17 Porch.mp3"
}
] ]
}, },
{ {
"title": "", "title": "Biography (from Education of the Girlchild)",
"details": "Big Ego Records, 1978",
"parent_dir": "music_sound_various", "parent_dir": "music_sound_various",
"media": "MONK MIX Remixes and Interpretations of Music by Meredith Monk CD2", "media": "Biography (from Education of the Girlchild)_Big Ego Records, 1978",
"image": "Biography (from Education of the Girlchild)_Big Ego Records, 1978.jpg",
"album": true, "album": true,
"tracks": [ "tracks": [
"01 Dawn (feat. Dj Spooky Remix).mp3", {
"02 Rain (feat. Vijay Iyer Revelation Mix Featuring Latasha N. Nevada Diggs).mp3", "title": "_63_13 Biography_Big Ego album",
"03 Atlas Ascending (feat. Todd Reynolds Remix).mp3", "media": "_63_13 Biography_Big Ego album.m4a"
"04 Epic (feat. Dj Rekha & Raj Star Remix).mp3", }
"05 Memory Song (feat. Miho Hatori Remix).mp3",
"06 Travellers (feat. King Britt Idm Mix).mp3",
"07 Night Vs. Lullaby (feat. Matt Marks Remix).mp3",
"08 Double Fiesta (feat. Arto Lindsay Remix).mp3",
"09 Long Shadows (feat. Scanner Entwine Mix).mp3",
"10 Dolmen Music, Part 1 (feat. Shodekeh's Embody & Continuums Remix).mp3",
"11 Braid (feat. High Priest _ Hprizm Ghostlover Remix).mp3",
"12 Vocal_ Mill Feuille (feat. Sussan Deyhim Remix).mp3"
] ]
}, },
{ {
"title": "", "title": "KEY: an albm of invisible theater",
"details": "Lovely Music, 1971",
"parent_dir": "music_sound_various", "parent_dir": "music_sound_various",
"media": "_55 Songs from the Hill-Tablet", "media": "KEY: an albm of invisible theater_Lovely Music, 1971",
"image": "KEY: an albm of invisible theater_Lovely Music, 1971.jpg",
"album": true, "album": true,
"tracks": [ "tracks": [
"_55_01 Lullaby.mp3", {
"_55_02 Mesa.mp3", "title": "_57_01 Porch",
"_55_03 Jade (old woman's song).mp3", "media": "_57_01 Porch.mp3"
"_55_04 Wa-lie-oh.mp3", },
"_55_05 Insect.mp3", {
"_55_06 Descending.mp3", "title": "_57_02 Understreet",
"_55_07 Silo.mp3", "media": "_57_02 Understreet.mp3"
"_55_08 Bird Code.mp3", },
"_55_09 Jew's Harp.mp3", {
"_55_10 Prairie Ghost.mp3", "title": "_57_03 What Does It Mean_",
"_55_11 Tablet.mp3" "media": "_57_03 What Does It Mean_.mp3"
},
{
"title": "_57_04 Vision #1",
"media": "_57_04 Vision #1.mp3"
},
{
"title": "_57_05 Fat Stream",
"media": "_57_05 Fat Stream.mp3"
},
{
"title": "_57_06 Vision #2",
"media": "_57_06 Vision #2.mp3"
},
{
"title": "_57_07 Do You Be_",
"media": "_57_07 Do You Be_.mp3"
},
{
"title": "_57_08 Vision (#3)",
"media": "_57_08 Vision (#3).mp3"
},
{
"title": "_57_09 Change",
"media": "_57_09 Change.mp3"
},
{
"title": "_57_10 Dungeon",
"media": "_57_10 Dungeon.mp3"
}
] ]
}, },
{ {
"title": "", "title": "MEMORY GAME",
"details": "Cantaloupe Music, 2020",
"parent_dir": "music_sound_various", "parent_dir": "music_sound_various",
"media": "_56 Our Lady of Late", "media": "MEMORY GAME_Cantaloupe Music, 2020",
"image": "MEMORY GAME_Cantaloupe Music, 2020.jpg",
"album": true, "album": true,
"tracks": [ "tracks": [
"_56_01 Prologue.mp3", {
"_56_02 Unison.mp3", "title": "_58_01 Spaceship",
"_56_03 Knee.mp3", "media": "_58_01 Spaceship.mp3"
"_56_04 Hey Rhythm.mp3", },
"_56_05 Cow Song.mp3", {
"_56_06 Sigh.mp3", "title": "_58_02 Gamemaster's Song",
"_56_07 Morning.mp3", "media": "_58_02 Gamemaster's Song.mp3"
"_56_08 Slide.mp3", },
"_56_09 Waltz.mp3", {
"_56_10 Prophecy.mp3", "title": "_58_03 Migration",
"_56_11 Dumb.mp3", "media": "_58_03 Migration.mp3"
"_56_12 Conversation.mp3", },
"_56_13 Low Ring.mp3", {
"_56_14 High Ring.mp3", "title": "_58_04 Memory Song",
"_56_15 Free.mp3", "media": "_58_04 Memory Song.mp3"
"_56_16 Edge.mp3", },
"_56_17 Scale Down.mp3", {
"_56_18 Epilogue.mp3" "title": "_58_05 Downfall",
"media": "_58_05 Downfall.mp3"
},
{
"title": "_58_06 Waltz In 5s",
"media": "_58_06 Waltz In 5s.mp3"
},
{
"title": "_58_07 Tokyo CHa Cha",
"media": "_58_07 Tokyo CHa Cha.mp3"
},
{
"title": "_58_08 Totentanz",
"media": "_58_08 Totentanz.mp3"
},
{
"title": "_58_09 Double Fiesta",
"media": "_58_09 Double Fiesta.mp3"
}
] ]
}, },
{ {
"title": "", "title": "MONK MIX Remixes and Interpretations of Music by Meredith Monk (CD1)",
"details": "The House Foundation for the Arts, 2012",
"parent_dir": "music_sound_various", "parent_dir": "music_sound_various",
"media": "_57 KEY-an album of invisible theater", "media": "MONK MIX Remixes and Interpretations of Music by Meredith Monk (CD1)_The House Foundation for the Arts, 2012",
"image": "MONK MIX Remixes and Interpretations of Music by Meredith Monk (CD1)_The House Foundation for the Arts, 2012.jpg",
"album": true, "album": true,
"tracks": [ "tracks": [
"_57_01 Porch.mp3", {
"_57_02 Understreet.mp3", "title": "01 Gotham Lullaby (Featuring Björk With The Brodsky Quartet)",
"_57_03 What Does It Mean_.mp3", "media": "01 Gotham Lullaby (Featuring Björk With The Brodsky Quartet).mp3"
"_57_04 Vision #1.mp3", },
"_57_05 Fat Stream.mp3", {
"_57_06 Vision #2.mp3", "title": "02 Caldera Chimera (Featuring Gabriel Prokofiev Remix)",
"_57_07 Do You Be_.mp3", "media": "02 Caldera Chimera (Featuring Gabriel Prokofiev Remix).mp3"
"_57_08 Vision (#3).mp3", },
"_57_09 Change.mp3", {
"_57_10 Dungeon.mp3" "title": "03 Click Song #1 (Featuring Don Byron)",
"media": "03 Click Song #1 (Featuring Don Byron).mp3"
},
{
"title": "04 Double Fiesta (Featuring Meredith Monk & Bang On A Can)",
"media": "04 Double Fiesta (Featuring Meredith Monk & Bang On A Can).mp3"
},
{
"title": "05 Astronaut Anthem (Featuring Sakamoto Remix)",
"media": "05 Astronaut Anthem (Featuring Sakamoto Remix).mp3"
},
{
"title": "06 Shaking (Featuring Lukas Ligeti) [Pyrolator Remix]",
"media": "06 Shaking (Featuring Lukas Ligeti) [Pyrolator Remix].mp3"
},
{
"title": "07 Last Song (Featuring Caetano Veloso)",
"media": "07 Last Song (Featuring Caetano Veloso).mp3"
},
{
"title": "08 Fat Stream (Featuring Nico_s Piano Homage)",
"media": "08 Fat Stream (Featuring Nico_s Piano Homage).mp3"
},
{
"title": "09 Wheel (Featuring John Hollenbeck & Theo Bleckmann)",
"media": "09 Wheel (Featuring John Hollenbeck & Theo Bleckmann).mp3"
},
{
"title": "10 Scared Song (Featuring Pamela Z)",
"media": "10 Scared Song (Featuring Pamela Z).mp3"
},
{
"title": "11 Boat Song (Featuring Rubin Kodheli)",
"media": "11 Boat Song (Featuring Rubin Kodheli).mp3"
},
{
"title": "12 Gathering (Featuring Lee Ranaldo Remix)",
"media": "12 Gathering (Featuring Lee Ranaldo Remix).mp3"
},
{
"title": "13 Evening (Featuring Henry Grimes Remix)",
"media": "13 Evening (Featuring Henry Grimes Remix).mp3"
}
] ]
}, },
{ {
"title": "", "title": "MONK MIX Remixes and Interpretations of Music by Meredith Monk (CD2)",
"details": "The House Foundation for the Arts, 2012",
"parent_dir": "music_sound_various", "parent_dir": "music_sound_various",
"media": "_58 MEMORY GAME", "media": "MONK MIX Remixes and Interpretations of Music by Meredith Monk (CD2)_The House Foundation for the Arts, 2012",
"image": "MONK MIX Remixes and Interpretations of Music by Meredith Monk (CD2)_The House Foundation for the Arts, 2012.jpg",
"album": true, "album": true,
"tracks": [ "tracks": [
"_58_01 Spaceship.mp3", {
"_58_02 Gamemaster's Song.mp3", "title": "01 Dawn (feat. Dj Spooky Remix)",
"_58_03 Migration.mp3", "media": "01 Dawn (feat. Dj Spooky Remix).mp3"
"_58_04 Memory Song.mp3", },
"_58_05 Downfall.mp3", {
"_58_06 Waltz In 5s.mp3", "title": "02 Rain (feat. Vijay Iyer Revelation Mix Featuring Latasha N. Nevada Diggs)",
"_58_07 Tokyo CHa Cha.mp3", "media": "02 Rain (feat. Vijay Iyer Revelation Mix Featuring Latasha N. Nevada Diggs).mp3"
"_58_08 Totentanz.mp3", },
"_58_09 Double Fiesta.mp3" {
"title": "03 Atlas Ascending (feat. Todd Reynolds Remix)",
"media": "03 Atlas Ascending (feat. Todd Reynolds Remix).mp3"
},
{
"title": "04 Epic (feat. Dj Rekha & Raj Star Remix)",
"media": "04 Epic (feat. Dj Rekha & Raj Star Remix).mp3"
},
{
"title": "05 Memory Song (feat. Miho Hatori Remix)",
"media": "05 Memory Song (feat. Miho Hatori Remix).mp3"
},
{
"title": "06 Travellers (feat. King Britt Idm Mix)",
"media": "06 Travellers (feat. King Britt Idm Mix).mp3"
},
{
"title": "07 Night Vs. Lullaby (feat. Matt Marks Remix)",
"media": "07 Night Vs. Lullaby (feat. Matt Marks Remix).mp3"
},
{
"title": "08 Double Fiesta (feat. Arto Lindsay Remix)",
"media": "08 Double Fiesta (feat. Arto Lindsay Remix).mp3"
},
{
"title": "09 Long Shadows (feat. Scanner Entwine Mix)",
"media": "09 Long Shadows (feat. Scanner Entwine Mix).mp3"
},
{
"title": "10 Dolmen Music, Part 1 (feat. Shodekeh's Embody & Continuums Remix)",
"media": "10 Dolmen Music, Part 1 (feat. Shodekeh's Embody & Continuums Remix).mp3"
},
{
"title": "11 Braid (feat. High Priest _ Hprizm Ghostlover Remix)",
"media": "11 Braid (feat. High Priest _ Hprizm Ghostlover Remix).mp3"
},
{
"title": "12 Vocal_ Mill Feuille (feat. Sussan Deyhim Remix)",
"media": "12 Vocal_ Mill Feuille (feat. Sussan Deyhim Remix).mp3"
}
] ]
}, },
{ {
"title": "", "title": "Monk and the Abbess",
"details": "BMG, 1996",
"parent_dir": "music_sound_various", "parent_dir": "music_sound_various",
"media": "_60 Beginnings", "media": "Monk and the Abbess_BMG, 1996",
"image": "Monk and the Abbess_BMG, 1996.jpg",
"album": true, "album": true,
"tracks": [ "tracks": [
"_60_01 Greensleeves.mp3", {
"_60_02 Nota.mp3", "title": "_62_05 Dawn (1985)",
"_60_03 Duet For Voice And Echoplex.mp3", "media": "_62_05 Dawn (1985).mp3"
"_60_04 Candy Bullets And Moon.mp3", },
"_60_05 Trance.mp3", {
"_60_06 Epic I.mp3", "title": "_62_06 Quarry weave 1 (1976)",
"_60_07 Paris.mp3", "media": "_62_06 Quarry weave 1 (1976).mp3"
"_60_08 Biography.mp3", },
"_60_09 Mill.mp3", {
"_60_10 The Tale.mp3", "title": "_62_07 Quarry lullaby (1976)",
"_60_11 Quarry Weave.mp3", "media": "_62_07 Quarry lullaby (1976).mp3"
"_60_12 Epic II.mp3", },
"_60_13 Tower.mp3", {
"_60_14 Mill.mp3", "title": "_62_08 Quarry weave 2 (1976)",
"_60_15 Do You Be_.mp3", "media": "_62_08 Quarry weave 2 (1976).mp3"
"_60_16 Quarry Procession.mp3", },
"_60_17 Porch.mp3" {
"title": "_62_09 Farmer's song (1974)",
"media": "_62_09 Farmer's song (1974).mp3"
},
{
"title": "_62_10 Astronaut anthem (1983)",
"media": "_62_10 Astronaut anthem (1983).mp3"
},
{
"title": "_62_11 Nightfall (1995)",
"media": "_62_11 Nightfall (1995).mp3"
}
] ]
}, },
{ {
"title": "", "title": "Our Lady of Late",
"details": "Minona Records, 1974 and Wergo, 1986",
"parent_dir": "music_sound_various", "parent_dir": "music_sound_various",
"media": "_61 Radio Songs", "media": "Our Lady of Late_Minona Records, 1974 and Wergo, 1986",
"image": "Our Lady of Late_Minona Records, 1974 and Wergo, 1986.jpg",
"album": true, "album": true,
"tracks": [ "tracks": [
"_61_01 Quarry Radio.mp3", {
"_61_02 Gotham Blues.mp3", "title": "_56_01 Prologue",
"_61_03 Quarry Waltz.mp3", "media": "_56_01 Prologue.mp3"
"_61_04 Gotham Lullaby.mp3" },
{
"title": "_56_02 Unison",
"media": "_56_02 Unison.mp3"
},
{
"title": "_56_03 Knee",
"media": "_56_03 Knee.mp3"
},
{
"title": "_56_04 Hey Rhythm",
"media": "_56_04 Hey Rhythm.mp3"
},
{
"title": "_56_05 Cow Song",
"media": "_56_05 Cow Song.mp3"
},
{
"title": "_56_06 Sigh",
"media": "_56_06 Sigh.mp3"
},
{
"title": "_56_07 Morning",
"media": "_56_07 Morning.mp3"
},
{
"title": "_56_08 Slide",
"media": "_56_08 Slide.mp3"
},
{
"title": "_56_09 Waltz",
"media": "_56_09 Waltz.mp3"
},
{
"title": "_56_10 Prophecy",
"media": "_56_10 Prophecy.mp3"
},
{
"title": "_56_11 Dumb",
"media": "_56_11 Dumb.mp3"
},
{
"title": "_56_12 Conversation",
"media": "_56_12 Conversation.mp3"
},
{
"title": "_56_13 Low Ring",
"media": "_56_13 Low Ring.mp3"
},
{
"title": "_56_14 High Ring",
"media": "_56_14 High Ring.mp3"
},
{
"title": "_56_15 Free",
"media": "_56_15 Free.mp3"
},
{
"title": "_56_16 Edge",
"media": "_56_16 Edge.mp3"
},
{
"title": "_56_17 Scale Down",
"media": "_56_17 Scale Down.mp3"
},
{
"title": "_56_18 Epilogue",
"media": "_56_18 Epilogue.mp3"
}
] ]
}, },
{ {
"title": "", "title": "Radio Songs",
"details": "The sound of White Columns, 2014",
"parent_dir": "music_sound_various", "parent_dir": "music_sound_various",
"media": "_62 Monk and the Abbiss", "media": "Radio Songs_The sound of White Columns, 2014",
"image": "Radio Songs_The sound of White Columns, 2014.jpg",
"album": true, "album": true,
"tracks": [ "tracks": [
"_62_05 Dawn (1985).mp3", {
"_62_06 Quarry weave 1 (1976).mp3", "title": "_61_01 Quarry Radio",
"_62_07 Quarry lullaby (1976).mp3", "media": "_61_01 Quarry Radio.mp3"
"_62_08 Quarry weave 2 (1976).mp3", },
"_62_09 Farmer's song (1974).mp3", {
"_62_10 Astronaut anthem (1983).mp3", "title": "_61_02 Gotham Blues",
"_62_11 Nightfall (1995).mp3" "media": "_61_02 Gotham Blues.mp3"
},
{
"title": "_61_03 Quarry Waltz",
"media": "_61_03 Quarry Waltz.mp3"
},
{
"title": "_61_04 Gotham Lullaby",
"media": "_61_04 Gotham Lullaby.mp3"
}
] ]
}, },
{ {
"title": "", "title": "Songs from the Hill-Tablet",
"details": "Wergo, 1979",
"parent_dir": "music_sound_various", "parent_dir": "music_sound_various",
"media": "_63 Biography (from Education of the Girlchild)", "media": "Songs from the Hill-Tablet_Wergo, 1979",
"image": "Songs from the Hill-Tablet_Wergo, 1979.jpg",
"album": true, "album": true,
"tracks": [ "tracks": [
"_63_13 Biography_Big Ego album.m4a" {
"title": "_55_01 Lullaby",
"media": "_55_01 Lullaby.mp3"
},
{
"title": "_55_02 Mesa",
"media": "_55_02 Mesa.mp3"
},
{
"title": "_55_03 Jade (old woman's song)",
"media": "_55_03 Jade (old woman's song).mp3"
},
{
"title": "_55_04 Wa-lie-oh",
"media": "_55_04 Wa-lie-oh.mp3"
},
{
"title": "_55_05 Insect",
"media": "_55_05 Insect.mp3"
},
{
"title": "_55_06 Descending",
"media": "_55_06 Descending.mp3"
},
{
"title": "_55_07 Silo",
"media": "_55_07 Silo.mp3"
},
{
"title": "_55_08 Bird Code",
"media": "_55_08 Bird Code.mp3"
},
{
"title": "_55_09 Jew's Harp",
"media": "_55_09 Jew's Harp.mp3"
},
{
"title": "_55_10 Prairie Ghost",
"media": "_55_10 Prairie Ghost.mp3"
},
{
"title": "_55_11 Tablet",
"media": "_55_11 Tablet.mp3"
}
] ]
} }
] ]

View File

@ -1 +0,0 @@
[]

View File

@ -1,63 +1,65 @@
[ [
{ {
"title": "Specimen Days", "title": "Cellular Songs",
"details": "The Public Theater, New York, 1981. 1:26:17", "details": "UCLA, Los Angeles, California, 2019",
"media": "-34 Specimen Days.m4v", "parent_dir": "theatre",
"media": "Cellular Songs_UCLA, Los Angeles, California, 2019.mp4",
"image": "Cellular Songs_UCLA, Los Angeles, California, 2019.jpg"
}, },
{ {
"title": "The Games: a science fiction opera", "title": "Education of the Girlchild",
"details": "Brooklyn Academy of Music (BAM), New York, 1984. 2:00:35", "details": "solo, Common Ground, NYC, 1973",
"media": "-37 MMonk_TheGames_BAM1984.m4v", "parent_dir": "theatre",
"media": "Education of the Girlchild_solo, Common Ground, NYC, 1973..m4v",
"image": "Education of the Girlchild_solo, Common Ground, NYC, 1973..jpg"
}, },
{ {
"title": "Facing North", "title": "Facing North",
"details": "George Washington University, Washington, DC, 1992. 54:32", "details": "George Washington University, Washington, DC, 1992",
}, "parent_dir": "theatre",
{ "media": "Facing North_George Washington University, Washington, DC, 1992.mp4",
"title": "The Politics of Quiet", "image": "Facing North_George Washington University, Washington, DC, 1992.jpg"
"details": "Brooklyn Academy of Music, BAM, New York, 1996. 1:43:46",
"media": "-36 The Politics of Quiet at BAM - 10.13.96.m4v",
}, },
{ {
"title": "Magic Frequencies (excerpts)", "title": "Magic Frequencies (excerpts)",
"details": "Muffathalle, Munich, Germany, 1998. 25:49", "details": "Muffathalle, Munich, Germany, 1998",
"media": "-42 1998 Magic Frequencies (excerpts) BetaCamSP transfer 2021.m4v" "parent_dir": "theatre",
"media": "Magic Frequencies (excerpts)_Muffathalle, Munich, Germany, 1998.m4v",
"image": "Magic Frequencies (excerpts)_Muffathalle, Munich, Germany, 1998.jpg"
}, },
{ {
"title":"A Celebration Service", "title": "Meredith Monk",
"details": "St. Marks Church, NYC, 1999. 55:02", "details": "A Celebration Service",
"media": "-35 Meredith Monk - A Celebration Service_ Danspace at St. Mark's Church_1999.m4v", "parent_dir": "theatre",
"media": "Meredith Monk_A Celebration Service_ Danspace at St. Mark's Church_1999.m4v",
"image": "Meredith Monk_A Celebration Service_ Danspace at St. Mark's Church_1999.jpg"
}, },
{ {
impermanence, Brooklyn Academy of Music (BAM), New York, 2006. 1:22:03 "title": "Songs of Ascension",
"details": "Brooklyn Academy of Music (BAM), New York, 2009",
"parent_dir": "theatre",
"media": "Songs of Ascension_Brooklyn Academy of Music (BAM), New York, 2009.mp4",
"image": "Songs of Ascension_Brooklyn Academy of Music (BAM), New York, 2009.jpg"
}, },
{ {
Songs of Ascension, Brooklyn Academy of Music (BAM), New York, 2009. 1:17:29 "title": "Specimen Days",
"details": "The Public Theater, New York, 1981",
"parent_dir": "theatre",
"media": "Specimen Days_The Public Theater, New York, 1981.m4v",
"image": "Specimen Days_The Public Theater, New York, 1981.jpg"
}, },
{ {
"title": "On Behalf of Nature", "title": "The Games",
"details": "UCLA, Los Angeles, California, 2013. 1:14:55" "details": "a science fiction opera, Brooklyn Academy of Music (BAM), New York, 1984",
"media": "" "parent_dir": "theatre",
"media": "The Games_a science fiction opera, Brooklyn Academy of Music (BAM), New York, 1984.m4v",
"image": "The Games_a science fiction opera, Brooklyn Academy of Music (BAM), New York, 1984.jpg"
},
{
"title": "impermanence",
"details": "Brooklyn Academy of Music (BAM), New York, 2006",
"parent_dir": "theatre",
"media": "impermanence_Brooklyn Academy of Music (BAM), New York, 2006.mp4",
"image": "impermanence_Brooklyn Academy of Music (BAM), New York, 2006.jpg"
} }
{
Cellular Songs, UCLA, Los Angeles, California, 2019. 1:24:07
}
"media": "-29 Education of the Gildchild solo_Common Ground_1973.m4v",
"media": "-30 Quarry_full film_1977_FINAL_20200127_added to dropbox.m4v",
"media": "-33 MEREDITH_MONK_SOLO_CONCERT_1980.m4v",
"media": "-38 MeredithMonk_SoloConcert_College des Bernadins Paris_20120516.m4v",
"media": "-39 Meredith Monk with Katie Geissinger in Concert, Haus der Kunst, Munich, Germany (2012).m4v",
"media": "-40 Candy_Bullets_and_Moon.m4v",
"media": "-41 Monk_Anthem_Final.m4v",
] ]

View File

@ -1,6 +1,29 @@
import fs from 'fs/promises' import fs from 'fs/promises'
import path from 'path' import path from 'path'
import { exec } from 'child_process'
// WAVEFORM GENERATION
// TODO@mx use node-js library
async function generateWaveform(audioFile, outputDir) {
const inputFile = path.join(outputDir, audioFile)
audioFile = audioFile.replace(/\.[^/.]+$/, '.png')
const outputPath = path.join(outputDir || '.', audioFile)
return new Promise((resolve, reject) => {
exec(
`audiowaveform -i '${inputFile}' -o '${outputPath}' --output-format png -z auto -w 640 -h 180 --background-color 00000000 --waveform-color FFFFFF50 --waveform-style bars --bar-style rounded --border-color 00000000 --bar-width 4 --bar-gap 6 --no-axis-labels`,
(error, stdOut) => {
if (error) {
console.error(error)
reject(error)
} else {
resolve(audioFile)
}
})
})
}
// MAIN FUNCTION
async function main() { async function main() {
// variable for incoming variable // variable for incoming variable
let mediaDir = '' let mediaDir = ''
@ -8,90 +31,189 @@ async function main() {
let recurse = false let recurse = false
let imagesOnly = false let imagesOnly = false
let dryRun = false let dryRun = false
let formatted = null
let waveform = false
let update = []
// get command line arguments // get command line arguments
process.argv.forEach(function (val, index) { process.argv.forEach(function (val, index) {
// console.log(index + ': ' + val) // console.log(index + ': ' + val)
if (val == '-dir') { switch (val) {
// console.log(process.argv[index + 1]) case '-dir':
mediaDir = process.argv[index + 1] || '' mediaDir = process.argv[index + 1] || ''
} break
case '-o':
outputDir = process.argv[index + 1] || ''
break
case '-u':
update = process.argv[index + 1].match(/(title|details|media|image[s]*|tracks|)/i)
? process.argv[index + 1]
.split(',')
.filter(a => [
'title',
'details',
'media',
'image',
'tracks',
'images'
].includes(a))
: true
break
case '-r':
recurse = true
break
case '--images-only':
imagesOnly = true
break
case '--dry-run':
dryRun = true
break
case '--formatted':
formatted = ['parent', 'recurse', 'both' ].includes(process.argv[index + 1]) ? process.argv[index + 1] : 'both'
break
case '--gen-waveform':
waveform = true
break
if (val == '--dry-run') { default:
dryRun = true break
}
if (val == '--images') {
imagesOnly = true
}
if (val == '-r' || val == '-R') {
recurse = true
}
if (val == '-o') {
outputDir = process.argv[index + 1]
} }
}) })
// if no media dir jump out // if no media dir passed jump out
if (!mediaDir) { if (!mediaDir) {
console.log('no directory passed to script') console.log('no directory passed to script')
return 0 return 0
} }
//
// read directory
const dirList = await fs.readdir(mediaDir)
// variable to hold media // variable to hold media
let media = [] let media = []
let images = [] let images = []
// read directory
const dirList = await fs.readdir(mediaDir)
if (!recurse) { // If recursing we are making the "media" the directories where we will find the
// acutal media to playback or see
if (recurse) {
media = dirList.filter(i => !i.match(/\.[^/.]+$/))
} else {
media = dirList.filter(i => i.match(/.(mp\d|m\d\w)$/i)) media = dirList.filter(i => i.match(/.(mp\d|m\d\w)$/i))
images = dirList.filter(i => i.match(/.(jp\w*g|png)$/i))
} }
// create an array of images found in the directory as well
images = dirList.filter(i => i.match(/.(jp\w*g|png)$/i))
// clean up directory for json // clean up passed directory for json
let parent_dir = mediaDir let parent_dir = mediaDir
.match(/(?<=\/)(\w|\d)+\/*$/i)[0] .match(/(?<=\/)(\w|\d)+\/*$/i)[0]
.replace(/\/$/i, '') .replace(/\/$/i, '')
// set an output directory if not set by flags
if (!outputDir) { if (!outputDir) {
outputDir = '.' outputDir = '.'
} }
const obj = await Promise.all(media.map(async (m, i) => { // THE MAIN EVENT
const obj = await Promise.all(media.map(async m => {
// setup structure
const _r = { const _r = {
title: '', ...genMetadata(m, formatted == 'parent' || formatted == 'both'),
details: '',
parent_dir, parent_dir,
media: m, media: m,
image: images[i], image: ''
} }
// if we are scanning for images only, remove the 'image' property
// as it will be saved in the media property
if (imagesOnly) {
delete _r.image
} else {
if (waveform) {
// if waveform generate waveform
_r.image = await generateWaveform(m, mediaDir)
} else {
// if not compare the media name with the image name
// and if a match assign it to 'image' variable from earlier
_r.image = linkImage(m.replace(/\.[^/.]+$/, ''), images)
}
}
// if recurse (again) now we are looking for the media in the folders
if (recurse) { if (recurse) {
// create full path from the passed dir, and the recuring dir
const rDir = path.join(mediaDir, m) const rDir = path.join(mediaDir, m)
const files = await fs.readdir(rDir) // read the dir
let files = await fs.readdir(rDir)
// filterout any rogue folders
// generate the metadata for each file
files = files.filter(f => f.match(/\.[^/.]+$/))
files = files.map(f => {
const { title, details } = genMetadata(f, formatted == 'recurse' || formatted == 'both')
return { title, details, media: f }
})
_r.album = true // if images only
_r.tracks = files if (imagesOnly) {
// setup object per image in array
_r.images = files
} else {
_r.album = true
_r.tracks = files
}
} }
// return the result to the 'obj' variable
return _r return _r
})) }))
const json = JSON.stringify(obj, null, 2)
let output = `${parent_dir}.json`
// turn the obj variable into JSON
const json = JSON.stringify(obj, null, 2)
// set the output filename
let outputFile = `${parent_dir}.json`
// check if file already exists at location
// const dataFile = await fs.readFile(path.join(outputDir, outputFile), 'utf8')
// console.log(JSON.parse(dataFile) || 'no data file exists')
// update only new fields
// if dryrun
if (dryRun) { if (dryRun) {
// just console it out
console.log(json) console.log(json)
} else { } else {
fs.writeFile(path.join(outputDir, output), json) // write it to a file
fs.writeFile(path.join(outputDir, outputFile), json)
} }
} }
function genMetadata(dir, formatted) {
let title, details
// if formatted flag set
if (formatted) {
// split the incoming name from the map on the '_'
// and set the first element to title, and the second to details
let [ t, d ] = dir.split('_')
// assign to return object
title = t
details = d ? d.replace(/\.+(mp\d|m\dv)$/i, '') : ''
} else {
// else just set title to filename without extensions
title = dir.replace(/\.[^/.]+$/, '')
}
return { title, details }
}
function linkImage(name, images) {
return images.find(i => {
return i.replace(/\.[^/.]+$/, '') == name
})
}
main() main()

View File

@ -1,5 +1,6 @@
import { LitElement, css, html, unsafeCSS } from 'lit' import { LitElement, css, html, unsafeCSS } from 'lit'
import Router from './api/Router.js' import Router from './api/Router.js'
import { Task } from '@lit-labs/task'
import MainCSS from './assets/styles/main.scss?inline' import MainCSS from './assets/styles/main.scss?inline'
@ -10,13 +11,17 @@ import './components/Footer.js'
import './assets/styles/main.scss' import './assets/styles/main.scss'
export class App extends LitElement { export class App extends LitElement {
static properties = {} static properties = {
dialogEl: { state: true }
}
constructor() { constructor() {
super() super()
} }
firstUpdated() { firstUpdated() {
this.dialogEl = this.shadowRoot.querySelector('dialog')
Router.addEventListener('route-changed', () => { Router.addEventListener('route-changed', () => {
if ('startViewTransition' in document) { if ('startViewTransition' in document) {
return document.startViewTransition(() => { return document.startViewTransition(() => {
@ -28,6 +33,27 @@ export class App extends LitElement {
} }
}) })
this.addEventListener('single-fullscreen-image', ({ detail }) => {
const { src, details } = detail
const img = new Image()
img.src = src
this.dialogEl.appendChild(img)
if (detail.details) {
const div = document.createElement('div')
const p = document.createElement('p')
p.textContent = details
div.appendChild(p)
this.dialogEl.appendChild(div)
}
this.dialogEl.showModal()
this.dialogEl.addEventListener('click', () => {
this.dialogEl.close()
this.dialogEl.innerHTML = ''
}, { once: true })
})
} }
render() { render() {
@ -35,6 +61,8 @@ export class App extends LitElement {
${Router.route.path == '/' ? '' : html`<mm-header title=${Router.route.title}></mm-header>`} ${Router.route.path == '/' ? '' : html`<mm-header title=${Router.route.title}></mm-header>`}
${Router.render()} ${Router.render()}
${Router.route.path == '/' ? '' : html`<mm-footer path=${Router.route.path}></mm-footer>`} ${Router.route.path == '/' ? '' : html`<mm-footer path=${Router.route.path}></mm-footer>`}
<dialog class="popup">
</dialog>
` `
} }
@ -45,6 +73,26 @@ export class App extends LitElement {
flex-direction: column; flex-direction: column;
height: 100vh; height: 100vh;
} }
dialog {
margin: auto;
border: none;
& div {
padding-block-start: 1em;
font-size: 1.25em;
}
& img {
display: block;
max-height: calc(100vmin - 2em);
max-width: calc(100vmin - 2em);
}
}
dialog::backdrop {
background-color: #00000090;
}
` ] ` ]
} }

View File

@ -74,6 +74,7 @@ export default new Router({
short: 'Unpublished', short: 'Unpublished',
icon: 'headphones', icon: 'headphones',
group: 'Music & Sound', group: 'Music & Sound',
disabled: true,
plugins: [ plugins: [
lazy(() => import('../views/audio.js')) lazy(() => import('../views/audio.js'))
], ],
@ -81,23 +82,23 @@ export default new Router({
}, },
{ {
path: resolveRouterPath('theatre'), path: resolveRouterPath('theatre'),
title: 'Music - Theatre Works', title: 'Music-Theatre Works',
short: 'Theatre Works', short: 'Music-Theatre',
icon: 'headphones', icon: 'film',
plugins: [ plugins: [
lazy(() => import('../views/audio.js')) lazy(() => import('../views/videos.js'))
], ],
render: () => html`<mm-audio></mm-audio>` render: () => html`<mm-videos></mm-videos>`
}, },
{ {
path: resolveRouterPath('concerts'), path: resolveRouterPath('concerts'),
title: 'Concert Recordings', title: 'Concert Recordings',
short: 'Concerts', short: 'Concerts',
icon: 'headphones', icon: 'film',
plugins: [ plugins: [
lazy(() => import('../views/audio.js')) lazy(() => import('../views/videos.js'))
], ],
render: () => html`<mm-audio></mm-audio>` render: () => html`<mm-videos></mm-videos>`
}, },
{ {
path: resolveRouterPath('films'), path: resolveRouterPath('films'),
@ -114,6 +115,7 @@ export default new Router({
title: 'Scores, Posters, Ephemera', title: 'Scores, Posters, Ephemera',
short: 'images', short: 'images',
icon: 'camera', icon: 'camera',
disabled: true,
plugins: [ plugins: [
lazy(() => import('../views/images.js')) lazy(() => import('../views/images.js'))
], ],

View File

@ -6,7 +6,6 @@ body {
line-height: 1; line-height: 1;
font-size: 16px; font-size: 16px;
--neutral-900: hsl(0 0% 0%); --neutral-900: hsl(0 0% 0%);
--neutral-700: hsl(220 10% 44%); --neutral-700: hsl(220 10% 44%);
--neutral-400: hsl(220 10% 88%); --neutral-400: hsl(220 10% 88%);
@ -18,6 +17,7 @@ body {
--green-400: hsl(147 100% 34%); --green-400: hsl(147 100% 34%);
--green-200: hsl(147 100% 50%); --green-200: hsl(147 100% 50%);
--neutral-gradient-600: linear-gradient(to top,rgba(208,210,214,1) 0%, rgba(141,142,145,1) 100%);
--neutral-gradient-400: linear-gradient(to bottom, hsl(228, 5%, 82%) 0%, var(--neutral-400) 100%); --neutral-gradient-400: linear-gradient(to bottom, hsl(228, 5%, 82%) 0%, var(--neutral-400) 100%);
--green-gradient-400: linear-gradient(to bottom, var(--green-400) 0%, var(--green-500) 100%); --green-gradient-400: linear-gradient(to bottom, var(--green-400) 0%, var(--green-500) 100%);
--green-inside-gradient-400: linear-gradient(to bottom, rgba(0, 154, 69, 1) 0%,rgba(177, 178, 181, 0) 100%), linear-gradient(rgba(0, 173, 78, 1),rgba(0, 173, 78, 1)); --green-inside-gradient-400: linear-gradient(to bottom, rgba(0, 154, 69, 1) 0%,rgba(177, 178, 181, 0) 100%), linear-gradient(rgba(0, 173, 78, 1),rgba(0, 173, 78, 1));

View File

@ -19,18 +19,26 @@ class AudioCard extends LitElement {
} }
clickHandler() { clickHandler() {
const event = new CustomEvent('select-audio', { if (!this.selected) {
bubbles: true, composed: true, const event = new CustomEvent('select-audio', {
detail: { ...this.details, idx: this.idx } bubbles: true, composed: true,
}) detail: { ...this.details, idx: this.idx }
})
this.dispatchEvent(event) this.dispatchEvent(event)
}
} }
render() { render() {
return html` return html`
<div class="card ${this.selected ? 'selected' : ''}" @click=${this.clickHandler}> <div class="card ${this.selected ? 'selected' : ''}" @click=${this.clickHandler}>
<mm-icon name=${this.icon}></mm-icon> ${this.details.album && this.details.image
? html`<img
class="albumCover"
src=${this.details.image}
/>`
: html`<mm-icon name=${this.icon}></mm-icon>`
}
<main> <main>
<p class="heading">${this.details?.title}</p> <p class="heading">${this.details?.title}</p>
@ -54,10 +62,18 @@ class AudioCard extends LitElement {
font-size: 3em; font-size: 3em;
} }
.albumCover {
display: block;
height: 3em;
aspect-ratio: 1 / 1;
object-fit: cover;
background: lightgrey;
}
.card { .card {
display: grid; display: grid;
grid-auto-flow: column; grid-auto-flow: column;
grid-template-columns: min-content 1fr; grid-template-columns: auto 1fr;
gap: 1em; gap: 1em;
align-items: center; align-items: center;
padding-inline: 0.75em; padding-inline: 0.75em;
@ -73,6 +89,10 @@ class AudioCard extends LitElement {
.card.selected { .card.selected {
outline: 2px solid var(--green-400, black); outline: 2px solid var(--green-400, black);
} }
.info {
max-width: 30ch;
}
` ] ` ]
} }

View File

@ -43,13 +43,13 @@ class Footer extends LitElement {
renderLink(r, first = false) { renderLink(r, first = false) {
return html` return html`
${first ? html`<span class="break"></span>` : '' } ${first && !r.disabled ? html`<span class="break"></span>` : '' }
<a href=${r.path} class="${r.path == this.path ? 'selected' : ''}"> ${!r.disabled ? html`<a href=${r.path} class="${r.path == this.path ? 'selected' : ''}">
<mm-icon name=${r.icon}></mm-icon> <mm-icon name=${r.icon}></mm-icon>
<span> <span>
${r.short} ${r.short}
</span> </span>
</a> </a>` : '' }
` `
} }

View File

@ -18,9 +18,8 @@ class HorizontalScroller extends LitElement {
static styles = css` static styles = css`
:host { :host {
--col-width: 10em; --col-width: 10em;
--gap: 1em; --gap: 0.75em;
display: flex; display: flex;
padding-block-end: 0.5em;
} }
.scroller { .scroller {

View File

@ -39,6 +39,7 @@ class ImageCarousel extends LitElement {
padding-inline-end: 0.5em; padding-inline-end: 0.5em;
padding-block-start: 0.25em; padding-block-start: 0.25em;
padding-block-end: 0.75em; padding-block-end: 0.75em;
background: black;
} }
mm-image { mm-image {

View File

@ -37,15 +37,19 @@ class ModularPlayer extends LitElement {
this._initAudio() this._initAudio()
} }
firstUpdated() {
console.log(this.details)
}
_getTrack = new Task( _getTrack = new Task(
this, this,
async () => { async () => {
try { try {
if (this.details) { if (this.details) {
if (this.details?.tracks) { if (this.details?.tracks) {
this.audio.src = `/media${this.details.media}/${this.details.tracks[this.track]}` this.audio.src = `${this.details.media}/${this.details.tracks[this.track].media}`
} else { } else {
this.audio.src = `/media${this.details.media}.mp3` this.audio.src = `${this.details.media}`
} }
this.audio.load() this.audio.load()
if (this.playing && this.audio.paused) { if (this.playing && this.audio.paused) {
@ -95,7 +99,7 @@ class ModularPlayer extends LitElement {
this.track = 0 this.track = 0
} }
this.audio.src = `/media${this.details.media}/${this.details.tracks[this.track]}` this.audio.src = `${this.details.media}/${this.details.tracks[this.track].media}`
this.audio.load() this.audio.load()
} }
}) })
@ -170,20 +174,35 @@ class ModularPlayer extends LitElement {
return `${percentage}%` return `${percentage}%`
} }
showImage() {
const event = new CustomEvent('single-fullscreen-image', {
bubbles: true, composed: true,
detail: { src: this.details.image }
})
this.dispatchEvent(event)
}
render() { render() {
return html` return html`
${ this.details ? ${ this.details ?
html`<div class="player"> html`<div class="player">
${this.details.tracks ? html` ${this.details.tracks ? html`
<div class="tracklist"> <div class="tracklist">
<h2>${this.details.title}</h2> <header>
<img src=${this.details.image}
@click=${this.showImage}
/>
<h2>${this.details.title}</h2>
</header>
<div class="list"> <div class="list">
<ul> <ul>
${this.details.tracks.map((t, i) => html` ${this.details.tracks.map((t, i) => html`
<li class="${this.track === i ? 'selected' : ''}" data-track-number=${i} @click=${this._selectTrack}> <li class="${this.track === i ? 'selected' : ''}" data-track-number=${i} @click=${this._selectTrack}>
<mm-icon name="notes" class=${this.playing ? 'playing' : ''}></mm-icon> <mm-icon name="notes" class=${this.playing ? 'playing' : ''}></mm-icon>
<span>${t}</span> <span>${t.title}</span>
</li>`)} </li>`)}
</ul> </ul>
</div> </div>
@ -191,7 +210,7 @@ ${ this.details ?
` : html``} ` : html``}
<header> <header>
<h2>${this.details.tracks ? this.details?.tracks[this.track] : this.details.title}</h2> <h2>${this.details.tracks ? this.details?.tracks[this.track].title : this.details.title}</h2>
${this.details.tracks ? '' : html`<p class="details">${this.details.details}</p>`} ${this.details.tracks ? '' : html`<p class="details">${this.details.details}</p>`}
</header> </header>
@ -216,7 +235,7 @@ ${ this.details ?
@input=${this._seekTrack} @input=${this._seekTrack}
class="progress" class="progress"
></mm-range> ></mm-range>
<img class="wav-img" loading="eager" src="/media${this.details.media}.png"> <img class="wav-img" loading="eager" src="${this.details.image}">
</div> </div>
` `
} }
@ -263,11 +282,11 @@ ${ this.details ?
} }
.player:has(.tracklist) { .player:has(.tracklist) {
grid-template-rows: 60% 0.5fr 0.3fr 1fr; grid-template-rows: 67% 0.5fr 0.3fr 1fr;
padding-block-end: 2em; padding-block-end: 2em;
gap: 0; gap: 0;
& header { & > header {
margin-block-start: 1.5em; margin-block-start: 1.5em;
margin-block-end: 2em; margin-block-end: 2em;
} }
@ -300,23 +319,36 @@ ${ this.details ?
.tracklist { .tracklist {
--tracklist-padding: calc(var(--padding) * 2); --tracklist-padding: calc(var(--padding) * 2);
position: relative; display: flex;
flex-direction: column;
background: var(--neutral-gradient-400); background: var(--neutral-gradient-400);
height: 100%; height: 100%;
> h2 { & header {
margin-inline: var(--tracklist-padding); margin-inline: var(--tracklist-padding);
margin-block-start: 0.75em; margin-block-start: 1em;
margin-block-end: 0.75em;
padding-block-start: 0.75em; padding-block-start: 0.75em;
border-block-start: thin solid var(--green-400); border-block-start: thin solid var(--green-400);
display: flex;
gap: 0.5em;
align-items: end;
color: black;
& img {
height: 4em;
aspect-ratio: 1 / 1;
}
& > h2 {
line-height: 1;
max-width: 15ch;
}
} }
> .list { & .list {
position: absolute; flex-grow: 1;
bottom: 0; margin-inline: var(--tracklist-padding);
top: calc(var(--tracklist-padding) * 2.5);
right: var(--tracklist-padding);
left: var(--tracklist-padding);
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
border-radius: 0.75em 0.75em 0 0; border-radius: 0.75em 0.75em 0 0;
@ -391,6 +423,7 @@ ${ this.details ?
} }
.info:has(.waveform) { .info:has(.waveform) {
& .time_pos, & .time_pos,
& .time_dur { & .time_dur {
position: absolute; position: absolute;

View File

@ -18,34 +18,42 @@ class NavCard extends LitElement {
firstUpdated() { firstUpdated() {
console.log(this.route) console.log(this.route)
this.shadowRoot.host.addEventListener('click', () => { this.shadowRoot.host.addEventListener('click', () => {
Router.navigate(this.route.path) if (!this.route.disabled) {
Router.navigate(this.route.path)
}
}) })
} }
navigate(path) { navigate(path) {
path = path || this.route.path if (!this.route.disabled) {
Router.navigate(path) path = path || this.route.path
Router.navigate(path)
}
} }
render() { render() {
return html` return html`
<div class="card"> <div class="card">
${Array.isArray(this.route) ? html` ${Array.isArray(this.route) ? html`
<p class="title">${this.route[0].group}</p> <header class="title">
<span>${this.route[0].group}</span>
</header>
${this.route.map(r => ${this.route.map(r =>
html` !r.disabled ? html`
<button @click=${() => this.navigate(r.path)}> <button @click=${() => this.navigate(r.path)}>
<mm-icon name=${r.icon}></mm-icon> <mm-icon name=${r.icon}></mm-icon>
${r.title} ${r.title}
</button> </button>
` ` : ''
)} )}
` `
: :
html` html`
<p class="title">${this.route?.title}</p> <header class="title">
<button class="iconOnly" @click=${() => this.navigate()}> <span>${this.route?.title}</span>
<mm-icon name=${this.route.icon}></mm-icon> </header>
<button class="iconOnly" @click=${this.navigate}>
${!this.route.disabled ? html`<mm-icon name=${this.route.icon}></mm-icon>` : html`<span>coming soon...</span>`}
</button> </button>
` `
} }
@ -88,6 +96,15 @@ class NavCard extends LitElement {
font-size: 0.75em; font-size: 0.75em;
text-align: center; text-align: center;
border-bottom: thin solid var(--highlight-color); border-bottom: thin solid var(--highlight-color);
display: flex;
align-items: center;
min-height: 2.5em;
& > span {
display: block;
max-width: 18ch;
margin-inline: auto;
}
} }
button { button {
@ -105,6 +122,10 @@ class NavCard extends LitElement {
text-shadow: 0 0 5px #ffffff; text-shadow: 0 0 5px #ffffff;
margin: 0; margin: 0;
&:has(span) {
background: var(--neutral-gradient-600);
}
&.iconOnly { &.iconOnly {
display: flex; display: flex;
gap: 0; gap: 0;
@ -112,6 +133,10 @@ class NavCard extends LitElement {
justify-content: center; justify-content: center;
font-size: 1.5em; font-size: 1.5em;
padding: 1em; padding: 1em;
& > span {
font-size: 0.5em;
}
} }
& mm-icon { & mm-icon {

View File

@ -13,6 +13,7 @@ class VerticalCard extends LitElement {
super() super()
this.details = {} this.details = {}
this.selected = false this.selected = false
} }
select() { select() {
@ -30,7 +31,7 @@ class VerticalCard extends LitElement {
return html` return html`
<div @click=${this.select} class=${this.selected ? 'selected' : ''}> <div @click=${this.select} class=${this.selected ? 'selected' : ''}>
<picture> <picture>
<img src="/media/${Router.route.path}/thumbs/${this.details.media}"> <img src="/media/${Router.route.path}/${this.details.image}">
</picture> </picture>
<aside> <aside>
<p class="title">${this.details?.title}</p> <p class="title">${this.details?.title}</p>
@ -51,7 +52,7 @@ class VerticalCard extends LitElement {
div { div {
height: 100%; height: 100%;
display: grid; display: grid;
grid-template-rows: 75% 25%; grid-template-rows: 72% 28%;
background: lightgrey; background: lightgrey;
line-height: 1; line-height: 1;
overflow: hidden; overflow: hidden;
@ -59,10 +60,11 @@ class VerticalCard extends LitElement {
background: var(--background-color); background: var(--background-color);
color: var(--font-color); color: var(--font-color);
align-content: start; align-content: start;
font-size: 1em;
} }
.selected { .selected {
outline: 2px solid var(--color); outline: 1px solid var(--color);
} }
picture { picture {

View File

@ -1,8 +1,6 @@
import { LitElement, css, html } from 'lit' import { LitElement, css, html } from 'lit'
import { Task } from '@lit-labs/task' import { Task } from '@lit-labs/task'
import Photo from '/images/Meredith Monk (1974) Photo Lauretta Harris.jpg'
// components // components
import '../components/Loading.js' import '../components/Loading.js'
import '../components/AudioCard.js' import '../components/AudioCard.js'
@ -27,7 +25,9 @@ class AudioView extends LitElement {
const div = this.shadowRoot.querySelector('mm-audio-player') const div = this.shadowRoot.querySelector('mm-audio-player')
div.addEventListener('transitionend', () => { div.addEventListener('transitionend', () => {
this.selected = { ...detail, media: `${Router.route.path}/${detail.media}` } this.selected = {
...detail
}
div.classList.remove('unloading') div.classList.remove('unloading')
}) })
@ -49,10 +49,19 @@ class AudioView extends LitElement {
const res = await fetch(`/data${Router.route.path}.json`) const res = await fetch(`/data${Router.route.path}.json`)
const json = await res.json() const json = await res.json()
this.tracks = json this.tracks = json.map(i => {
// start track (0 is not currently available) i.media = `/media${Router.route.path}/${i.media}`
const idx = 1 i.image = i.image ? `/media${Router.route.path}/${i.image}` : ''
this.selected = { ...this.tracks[idx], idx, media: `${Router.route.path}/${this.tracks[idx].media}` }
return i
})
if (this.tracks.length > 0) {
const idx = 0
this.selected = {
...this.tracks[idx], idx,
}
}
}, },
() => [] () => []
) )
@ -70,21 +79,20 @@ class AudioView extends LitElement {
<div class="scroll-items"> <div class="scroll-items">
${this._getAudio.render({ ${this._getAudio.render({
pending: () => html`<mm-loading style="--fill-color: grey"></mm-loading>`, pending: () => html`<mm-loading style="--fill-color: grey"></mm-loading>`,
complete: () => html`${this.tracks.map((t, i) => html` complete: () => html`${this.tracks.length > 0 ? this.tracks.map((t, i) => html`
<mm-acard <mm-acard
idx=${i} idx=${i}
icon=${t.album ? 'album' : 'play-circle'} icon=${t.album ? 'album' : 'play-circle'}
?selected=${t.title == this.selected.title && t.details == this.selected.details} ?selected=${t.title == this.selected.title && t.details == this.selected.details}
.details=${t} .details=${t}
></mm-acard>`)}`, ></mm-acard>`) : 'No tracks added for this category' }`,
error: (err) => html`Error: ${err}` error: (err) => html`Error: ${err}`
})} })}
</div> </div>
</nav> </nav>
<div class="player"> <div class="player ${this.selected.tracks ? '' : 'single'}">
<div> <div>
${this.selected?.tracks ? '' : html`<img class="bg-img" src=${Photo} />` }
<mm-audio-player .details=${this.selected}></mm-audio-player> <mm-audio-player .details=${this.selected}></mm-audio-player>
</div> </div>
</div> </div>
@ -126,6 +134,13 @@ class AudioView extends LitElement {
.player { .player {
position: relative; position: relative;
&.single > div {
background-image: url('/images/Meredith Monk (1974) Photo Lauretta Harris.jpg'), var(--green-gradient-400);
background-position: center;
background-size: 110%, 110%, cover;
background-blend-mode: soft-light, screen;
}
> div { > div {
position: absolute; position: absolute;
inset: 0; inset: 0;

View File

@ -30,10 +30,6 @@ class ImageView extends LitElement {
json.forEach(i => i.path = `/media${Router.route.path}/${i.media}`) json.forEach(i => i.path = `/media${Router.route.path}/${i.media}`)
this.images = json this.images = json
this.selected = this.images[0]
console.log(this.selected)
} catch (err) { } catch (err) {
console.error(err) console.error(err)
} }
@ -42,30 +38,54 @@ class ImageView extends LitElement {
) )
selectImage({ target }) { selectImage({ target }) {
const { details } = target console.log(target.details)
if (details?.title != this.selected?.title) { const event = new CustomEvent('single-fullscreen-image', {
this.selected = details bubbles: true, composed: true,
} detail: { ...target.details, src: `/media${Router.route.path}/${target.details.section}/${target.details.media}` }
})
this.dispatchEvent(event)
} }
render() { render() {
return html` return html`
<mm-img-carousel .images=${this.images}></mm-img-carousel> <!-- <mm-img-carousel .images=${this.images}></mm-img-carousel> -->
<mm-hscroller> ${this.images.length > 0 ?
${this._getImages.render({ complete: () => this.images.map(i => html` html`
<mm-vcard @click=${this.selectImage} .details=${i} ?selected=${i.title == this.selected?.title}></mm-vcard> <div class="gallery">
`) })} ${this._getImages.render({ complete: () => this.images.map(
</mm-hscroller> section => html`
<header class="section-header">
<h3>${section.title}</h3>
</header>
<main>
${section.images.map(
i => html`
<mm-vcard
@click=${this.selectImage}
.details=${ { ...i, section: section.media } }
?selected=${i.title == this.selected?.title}
>
</mm-vcard>
`)}
</main>
`
) })}
</div>
` :
html`<h2>No images added to the gallery</h2>`
}
` `
} }
static styles = [ css`${unsafeCSS(MainCSS)}`, css` static styles = [ css`${unsafeCSS(MainCSS)}`, css`
:host { :host {
flex-grow: 1; flex-grow: 1;
display: grid; height: 100%;
grid-template-rows: 1fr auto; overflow: auto;
gap: 0.5em; padding-inline: 1em;
padding-block: 1em;
} }
mm-hscroller { mm-hscroller {
@ -77,12 +97,34 @@ class ImageView extends LitElement {
--color: var(--green-400, lime); --color: var(--green-400, lime);
} }
header {
padding-block-start: 1.5em;
padding-block-end: 1em;
font-size: 1.25em;
border-bottom: 1px solid var(--green-400);
margin-block-end: 1em;
}
main {
display: grid;
gap: 0.75em;
grid-template-columns: repeat(7, 1fr);
grid-auto-rows: 10em;
}
.image { .image {
position: relative; position: relative;
inline-margin: auto; inline-margin: auto;
height: 100%; height: 100%;
} }
mm-image-carousel {
position: absolute;
inset: 0;
}
` ] ` ]
} }
customElements.define('mm-images', ImageView) customElements.define('mm-images', ImageView)

View File

@ -44,7 +44,7 @@ class VideoView extends LitElement {
<main> <main>
<aside> <aside>
<h2>${this.selected.title}</h2> <h2>${this.selected.title}</h2>
<p class="detail">${this.selected.detail}</p> <p class="details">${this.selected.details}</p>
</aside> </aside>
<mm-vplayer media='/media/${this.selected.media}' ?autoplay=${this.selected.autoplay}></mm-vplayer> <mm-vplayer media='/media/${this.selected.media}' ?autoplay=${this.selected.autoplay}></mm-vplayer>
</main> </main>
@ -55,7 +55,7 @@ class VideoView extends LitElement {
complete: () => html`${this.films.map(t => html` complete: () => html`${this.films.map(t => html`
<mm-vcard <mm-vcard
.details=${t} .details=${t}
?selected=${t.title == this.selected.title && t.detail == this.selected.detail} ?selected=${t.title == this.selected.title && t.details == this.selected.details}
></mm-vcard>` ></mm-vcard>`
)}` )}`
})} })}
@ -68,7 +68,7 @@ class VideoView extends LitElement {
flex-grow: 1; flex-grow: 1;
height: 100%; height: 100%;
display: grid; display: grid;
grid-template-rows: 70% 30%; grid-template-rows: 1fr 0.55fr;
gap: 0.25em; gap: 0.25em;
} }
@ -95,7 +95,7 @@ class VideoView extends LitElement {
margin-block-end: 0.2em; margin-block-end: 0.2em;
} }
> .detail { > .details {
line-height: 1.1; line-height: 1.1;
} }
} }

View File

@ -5,6 +5,12 @@ export default defineConfig({
build: { build: {
sourcemap: true, sourcemap: true,
target: [ 'esnext', 'edge100', 'firefox100', 'chrome100', 'safari18' ], target: [ 'esnext', 'edge100', 'firefox100', 'chrome100', 'safari18' ],
rollupOptions: {
external: [
'scripts',
'public/media'
],
},
}, },
server: { server: {
host: '0.0.0.0' host: '0.0.0.0'