[{"data":1,"prerenderedAt":4413},["ShallowReactive",2],{"navigation":3,"/blog/music-data/data-modeling-in-mongodb-with-the-usage-of-design-patterns-post":734,"/blog/music-data/data-modeling-in-mongodb-with-the-usage-of-design-patterns-surround":811,"/blog/music-data/data-modeling-in-mongodb-with-the-usage-of-design-patterns-related":816},[4,70,207,312,721],{"title":5,"path":6,"stem":7,"children":8,"page":69},"Case Study","/blog/case-study","blog/case-study",[9,13,17,21,25,29,33,37,41,45,49,53,57,61,65],{"title":10,"path":11,"stem":12},"Ambistream – Multi-Layer Streaming Platform","/blog/case-study/ambistream-building-a-multi-layer-streaming-platform-from-a-spark-of-an-idea","blog/case-study/ambistream-building-a-multi-layer-streaming-platform-from-a-spark-of-an-idea",{"title":14,"path":15,"stem":16},"Custom Chromecast & AirPlay Casting App","/blog/case-study/chromecast-airplay-casting-app-case-study","blog/case-study/chromecast-airplay-casting-app-case-study",{"title":18,"path":19,"stem":20},"Direct Music Licensing Platform Case Study","/blog/case-study/dlm-music-catalog-case-study","blog/case-study/dlm-music-catalog-case-study",{"title":22,"path":23,"stem":24},"Assembly Instructions App - Galeco Mobile App","/blog/case-study/galeco-mobile-app-case-study","blog/case-study/galeco-mobile-app-case-study",{"title":26,"path":27,"stem":28},"MemoSonic: Building an Audio Memory Game","/blog/case-study/how-we-built-memosonic-accessible-audio-memory-game-flutter","blog/case-study/how-we-built-memosonic-accessible-audio-memory-game-flutter",{"title":30,"path":31,"stem":32},"Loyalty Program App for Shopping Mall","/blog/case-study/loyalty-program-application-case-study","blog/case-study/loyalty-program-application-case-study",{"title":34,"path":35,"stem":36},"Mobile Application for Music Catalogs","/blog/case-study/mobile-app-for-music-catalog","blog/case-study/mobile-app-for-music-catalog",{"title":38,"path":39,"stem":40},"Universal Music Data Parser for 20+ Platforms","/blog/case-study/musicdata-lab-universal-music-data-parser-case-study","blog/case-study/musicdata-lab-universal-music-data-parser-case-study",{"title":42,"path":43,"stem":44},"Panther ML/AI Pricing Recommendation Tool","/blog/case-study/panther-pricing-recommendation-tool-case-study","blog/case-study/panther-pricing-recommendation-tool-case-study",{"title":46,"path":47,"stem":48},"4D Grupa Roofing Wholesalers Platform","/blog/case-study/roofing-wholesalers-website-case-study","blog/case-study/roofing-wholesalers-website-case-study",{"title":50,"path":51,"stem":52},"Talent Alpha HR Frontend Platform","/blog/case-study/talent-alpha-hr-platform-case-study","blog/case-study/talent-alpha-hr-platform-case-study",{"title":54,"path":55,"stem":56},"Ticketing & Events Platform Development","/blog/case-study/ticketing-events-platform-case-study","blog/case-study/ticketing-events-platform-case-study",{"title":58,"path":59,"stem":60},"Turn Fans into Superfans — Roadie.co","/blog/case-study/turn-fans-into-superfans-roadie-co","blog/case-study/turn-fans-into-superfans-roadie-co",{"title":62,"path":63,"stem":64},"Walkative 2.0 Global Booking Engine","/blog/case-study/walkative-2-booking-platform-case-study","blog/case-study/walkative-2-booking-platform-case-study",{"title":66,"path":67,"stem":68},"Why Merch Is the New Royalty Check, and How Tech Is Closing the Gap","/blog/case-study/why-merch-is-the-new-royalty-check","blog/case-study/why-merch-is-the-new-royalty-check",false,{"title":71,"path":72,"stem":73,"children":74,"page":69},"Music Data","/blog/music-data","blog/music-data",[75,79,83,87,91,95,99,103,107,111,115,119,123,127,131,135,139,143,147,151,155,159,163,167,171,175,179,183,187,191,195,199,203],{"title":76,"path":77,"stem":78},"13 Distributors, 5 File Formats, Zero Standards -The Reality of Music Royalty Data","/blog/music-data/13-distributors-5-file-formats-zero-standards-the-reality-of-music-royalty-data","blog/music-data/13-distributors-5-file-formats-zero-standards-the-reality-of-music-royalty-data",{"title":80,"path":81,"stem":82},"830 Ways to Say Spotify - Normalizing Music Streaming Data","/blog/music-data/830-ways-to-say-spotify-normalizing-music-streaming-data","blog/music-data/830-ways-to-say-spotify-normalizing-music-streaming-data",{"title":84,"path":85,"stem":86},"Why Music Companies Need AI-Powered Analytics (And How We Built One)","/blog/music-data/ai-powered-analytics-dashboard-django-clickhouse-ollama","blog/music-data/ai-powered-analytics-dashboard-django-clickhouse-ollama",{"title":88,"path":89,"stem":90},"AI Rehearsal: Spaced Repetition for Your Musical Ideas","/blog/music-data/ai-rehearsal-spaced-repetition-for-musical-ideas","blog/music-data/ai-rehearsal-spaced-repetition-for-musical-ideas",{"title":92,"path":93,"stem":94},"Audio Project Organization Is a Mess — Here's Why","/blog/music-data/audio-project-organization-mess","blog/music-data/audio-project-organization-mess",{"title":96,"path":97,"stem":98},"Why Audio Search Is Still Broken and How to Fix It with Embeddings","/blog/music-data/audio-search-broken-fix-with-embeddings","blog/music-data/audio-search-broken-fix-with-embeddings",{"title":100,"path":101,"stem":102},"AI Song Structure Analysis: Intro, Verse, Chorus","/blog/music-data/automatic-song-structure-analysis-how-ai-detects-intro-verse-chorus","blog/music-data/automatic-song-structure-analysis-how-ai-detects-intro-verse-chorus",{"title":104,"path":105,"stem":106},"The Broken Feedback Loop in Music Collaboration","/blog/music-data/broken-feedback-loop-music-collaboration","blog/music-data/broken-feedback-loop-music-collaboration",{"title":108,"path":109,"stem":110},"Building a Claude Skill for DDEX Validation: Automate Music Metadata Checks with AI","/blog/music-data/building-a-claude-skill-for-ddex-validation-music-metadata","blog/music-data/building-a-claude-skill-for-ddex-validation-music-metadata",{"title":112,"path":113,"stem":114},"Building a Custom Music Delivery Platform on the Revelator API","/blog/music-data/building-a-custom-music-delivery-platform-on-the-revelator-api","blog/music-data/building-a-custom-music-delivery-platform-on-the-revelator-api",{"title":116,"path":117,"stem":118},"Building a Suno AI Remix App with Nuxt & Firebase","/blog/music-data/building-a-suno-remix-app-with-nuxt-and-firebase","blog/music-data/building-a-suno-remix-app-with-nuxt-and-firebase",{"title":120,"path":121,"stem":122},"C2PA & DDEX: Authenticity Meets Rights in the Age of AI Music","/blog/music-data/c2pa-and-ddex-authenticity-meets-rights-in-the-age-of-ai-music","blog/music-data/c2pa-and-ddex-authenticity-meets-rights-in-the-age-of-ai-music",{"title":124,"path":125,"stem":126},"C2PA in Music: A Claude MCP for Reading Content Provenance","/blog/music-data/c2pa-in-music-mcp","blog/music-data/c2pa-in-music-mcp",{"title":128,"path":129,"stem":130},"Data Modeling in MongoDB Using Design Patterns","/blog/music-data/data-modeling-in-mongodb-with-the-usage-of-design-patterns","blog/music-data/data-modeling-in-mongodb-with-the-usage-of-design-patterns",{"title":132,"path":133,"stem":134},"Office Hours with MusicTech Lab's DDEX Expert","/blog/music-data/ddex-office-hours-musictech","blog/music-data/ddex-office-hours-musictech",{"title":136,"path":137,"stem":138},"DDEX Open Source Projects Review","/blog/music-data/ddex-open-source-projects-review","blog/music-data/ddex-open-source-projects-review",{"title":140,"path":141,"stem":142},"Extracting Data from Ableton .als and .asd Files","/blog/music-data/extracting-data-from-ableton-als-asd-files","blog/music-data/extracting-data-from-ableton-als-asd-files",{"title":144,"path":145,"stem":146},"Maciej Dulski on Sound Connections Podcast","/blog/music-data/from-startups-to-musictech-maciej-dulski-on-sound-connections-podcast","blog/music-data/from-startups-to-musictech-maciej-dulski-on-sound-connections-podcast",{"title":148,"path":149,"stem":150},"How to Transcribe Video to Text Using OpenAI Whisper","/blog/music-data/how-to-transcribe-video-to-text-using-whisper","blog/music-data/how-to-transcribe-video-to-text-using-whisper",{"title":152,"path":153,"stem":154},"Epidemic Sound MCP with Claude for Devs","/blog/music-data/how-to-use-epidemic-sound-mcp-with-claude","blog/music-data/how-to-use-epidemic-sound-mcp-with-claude",{"title":156,"path":157,"stem":158},"Hybrid Database Model in Django for Speed","/blog/music-data/hybrid-database-model-in-django-as-a-performance-booster","blog/music-data/hybrid-database-model-in-django-as-a-performance-booster",{"title":160,"path":161,"stem":162},"Introduction to generating DDEX file using Python","/blog/music-data/introduction-to-generating-ddex-file-using-python","blog/music-data/introduction-to-generating-ddex-file-using-python",{"title":164,"path":165,"stem":166},"Maintaining Music Tech Tools: The SLA Dilemma for Small Teams","/blog/music-data/maintaining-music-tech-tools-the-sla-dilemma-for-small-teams","blog/music-data/maintaining-music-tech-tools-the-sla-dilemma-for-small-teams",{"title":168,"path":169,"stem":170},"Querying Bandcamp Revenue Reports with Natural Language — Meet mtl-bandcamp-mcp","/blog/music-data/mtl-bandcamp-mcp-open-source-revenue-dashboard","blog/music-data/mtl-bandcamp-mcp-open-source-revenue-dashboard",{"title":172,"path":173,"stem":174},"mtl-metadata-mcp: Open Source Audio Metadata Embedding for Claude Code","/blog/music-data/mtl-metadata-mcp-open-source-audio-metadata-embedding","blog/music-data/mtl-metadata-mcp-open-source-audio-metadata-embedding",{"title":176,"path":177,"stem":178},"MusicTech Resources for Builders","/blog/music-data/musictech-resources-curated-insights-for-the-musictech-builders","blog/music-data/musictech-resources-curated-insights-for-the-musictech-builders",{"title":180,"path":181,"stem":182},"Poland's Creative Tech and MusicTech Rise","/blog/music-data/polands-creative-tech-sector-is-on-the-rise-and-musictech-is-part-of-it","blog/music-data/polands-creative-tech-sector-is-on-the-rise-and-musictech-is-part-of-it",{"title":184,"path":185,"stem":186},"Batch ISRC Enrichment That Turns Messy Catalogs Into Clean Data","/blog/music-data/scout-isrc-metadata-enrichment-spotify-musicbrainz","blog/music-data/scout-isrc-metadata-enrichment-spotify-musicbrainz",{"title":188,"path":189,"stem":190},"Music Self-Publishing: The Emuze.me Story","/blog/music-data/self-publishing-in-the-music-industry-a-tale-of-emuze-me","blog/music-data/self-publishing-in-the-music-industry-a-tale-of-emuze-me",{"title":192,"path":193,"stem":194},"Understanding the API First Approach","/blog/music-data/understanding-the-api-first-approach","blog/music-data/understanding-the-api-first-approach",{"title":196,"path":197,"stem":198},"The Voice Memo Graveyard Problem","/blog/music-data/voice-memo-graveyard-problem","blog/music-data/voice-memo-graveyard-problem",{"title":200,"path":201,"stem":202},"Which Database for Music Data? Redshift vs BigQuery vs ClickHouse and When to Use Each","/blog/music-data/which-database-for-music-data-redshift-vs-bigquery-vs-clickhouse","blog/music-data/which-database-for-music-data-redshift-vs-bigquery-vs-clickhouse",{"title":204,"path":205,"stem":206},"Why we decided to use wavesurfer.js","/blog/music-data/why-we-decided-to-use-wavesurfer","blog/music-data/why-we-decided-to-use-wavesurfer",{"title":208,"path":209,"stem":210,"children":211,"page":69},"Newsletter","/blog/newsletter","blog/newsletter",[212,216,220,224,228,232,236,240,244,248,252,256,260,264,268,272,276,280,284,288,292,296,300,304,308],{"title":213,"path":214,"stem":215},"Music Industry Tech Openings (April 2024 Update)","/blog/newsletter/music-industry-tech-openings-april-2024-update","blog/newsletter/music-industry-tech-openings-april-2024-update",{"title":217,"path":218,"stem":219},"Music Industry Tech Openings (April 2025 Update)","/blog/newsletter/music-industry-tech-openings-april-2025-update","blog/newsletter/music-industry-tech-openings-april-2025-update",{"title":221,"path":222,"stem":223},"Music Industry Tech Openings (August 2024 Update)","/blog/newsletter/music-industry-tech-openings-august-2024-update","blog/newsletter/music-industry-tech-openings-august-2024-update",{"title":225,"path":226,"stem":227},"Music Industry Tech Openings (December 2024 Update)","/blog/newsletter/music-industry-tech-openings-december-2024-update","blog/newsletter/music-industry-tech-openings-december-2024-update",{"title":229,"path":230,"stem":231},"Music Industry Tech Openings (February 2025 Update)","/blog/newsletter/music-industry-tech-openings-february-2025-update","blog/newsletter/music-industry-tech-openings-february-2025-update",{"title":233,"path":234,"stem":235},"Music Industry Tech Openings (January 2025 Update)","/blog/newsletter/music-industry-tech-openings-january-2025-update","blog/newsletter/music-industry-tech-openings-january-2025-update",{"title":237,"path":238,"stem":239},"Music Industry Tech Openings (July 2024 Update)","/blog/newsletter/music-industry-tech-openings-july-2024-update","blog/newsletter/music-industry-tech-openings-july-2024-update",{"title":241,"path":242,"stem":243},"Music Industry Tech Openings (June 2024 Update)","/blog/newsletter/music-industry-tech-openings-june-2024-update","blog/newsletter/music-industry-tech-openings-june-2024-update",{"title":245,"path":246,"stem":247},"Music Industry Tech Openings (March 2025 Update)","/blog/newsletter/music-industry-tech-openings-march-2025-update","blog/newsletter/music-industry-tech-openings-march-2025-update",{"title":249,"path":250,"stem":251},"Music Industry Tech Openings (May 2024 Update)","/blog/newsletter/music-industry-tech-openings-may-2024-update","blog/newsletter/music-industry-tech-openings-may-2024-update",{"title":253,"path":254,"stem":255},"Music Industry Tech Openings (May 2025 Update)","/blog/newsletter/music-industry-tech-openings-may-2025","blog/newsletter/music-industry-tech-openings-may-2025",{"title":257,"path":258,"stem":259},"Music Industry Tech Openings (November 2024 Update)","/blog/newsletter/music-industry-tech-openings-november-2024-update","blog/newsletter/music-industry-tech-openings-november-2024-update",{"title":261,"path":262,"stem":263},"Music Industry Tech Openings (October 2024 Update)","/blog/newsletter/music-industry-tech-openings-october-2024-update","blog/newsletter/music-industry-tech-openings-october-2024-update",{"title":265,"path":266,"stem":267},"Music Industry Tech Openings (September 2024 Update)","/blog/newsletter/music-industry-tech-openings-september-2024-update","blog/newsletter/music-industry-tech-openings-september-2024-update",{"title":269,"path":270,"stem":271},"MusicTech Insights #1 by Maciej Dulski","/blog/newsletter/musictech-insights-1-curated-by-maciej-dulski","blog/newsletter/musictech-insights-1-curated-by-maciej-dulski",{"title":273,"path":274,"stem":275},"Provenance, Not Detection, Is the Durable Answer","/blog/newsletter/musictech-insights-10-curated-by-jim-anderson","blog/newsletter/musictech-insights-10-curated-by-jim-anderson",{"title":277,"path":278,"stem":279},"What CMOs can teach us about innovation in uncertain times","/blog/newsletter/musictech-insights-10-curated-by-joanna-kurkowska","blog/newsletter/musictech-insights-10-curated-by-joanna-kurkowska",{"title":281,"path":282,"stem":283},"Feeling the MusicTech Momentum","/blog/newsletter/musictech-insights-2-curated-by-maciej-dulski","blog/newsletter/musictech-insights-2-curated-by-maciej-dulski",{"title":285,"path":286,"stem":287},"AI in Music: Hype, Hope, and a Human Touch","/blog/newsletter/musictech-insights-3-curated-by-drew-thurlow","blog/newsletter/musictech-insights-3-curated-by-drew-thurlow",{"title":289,"path":290,"stem":291},"The Music Metadata Conundrum","/blog/newsletter/musictech-insights-4-curated-by-amanda-schupf","blog/newsletter/musictech-insights-4-curated-by-amanda-schupf",{"title":293,"path":294,"stem":295},"7 Rounds in the First 10 Days of November 2025","/blog/newsletter/musictech-insights-5-curated-by-maciej-dulski","blog/newsletter/musictech-insights-5-curated-by-maciej-dulski",{"title":297,"path":298,"stem":299},"The End of an Era: It's All About to Crash","/blog/newsletter/musictech-insights-6-curated-by-sigurdur-arnason","blog/newsletter/musictech-insights-6-curated-by-sigurdur-arnason",{"title":301,"path":302,"stem":303},"Low-Code Magic Won't Solve MusicTech Reality","/blog/newsletter/musictech-insights-7-curated-by-mariusz-smenzyk","blog/newsletter/musictech-insights-7-curated-by-mariusz-smenzyk",{"title":305,"path":306,"stem":307},"The New Economics of Game Music","/blog/newsletter/musictech-insights-8-curated-by-kenny-vaughan","blog/newsletter/musictech-insights-8-curated-by-kenny-vaughan",{"title":309,"path":310,"stem":311},"Music Business Meets Direct-to-Fan","/blog/newsletter/musictech-insights-9-curated-by-yaw-asamani","blog/newsletter/musictech-insights-9-curated-by-yaw-asamani",{"title":313,"path":314,"stem":315,"children":316,"page":69},"Software Development","/blog/software-development","blog/software-development",[317,321,325,329,333,337,341,345,349,353,357,361,365,369,373,377,381,385,389,393,397,401,405,409,413,417,421,425,429,433,437,441,445,449,453,457,461,465,469,473,477,481,485,489,493,497,501,505,509,513,517,521,525,529,533,537,541,545,549,553,557,561,565,569,573,577,581,585,589,593,597,601,605,609,613,617,621,625,629,633,637,641,645,649,653,657,661,665,669,673,677,681,685,689,693,697,701,705,709,713,717],{"title":318,"path":319,"stem":320},"Benefits of Outsourcing Software Development","/blog/software-development/10-benefits-of-outsourcing-software-development-services","blog/software-development/10-benefits-of-outsourcing-software-development-services",{"title":322,"path":323,"stem":324},"10 Steps to Find the Best MVP Developers","/blog/software-development/10-steps-to-find-the-best-mvp-developers-for-your-startup-idea","blog/software-development/10-steps-to-find-the-best-mvp-developers-for-your-startup-idea",{"title":326,"path":327,"stem":328},"1,200 Looms Later: How Async Video Became My Development Superpower","/blog/software-development/1200-looms-how-async-video-became-our-development-superpower","blog/software-development/1200-looms-how-async-video-became-our-development-superpower",{"title":330,"path":331,"stem":332},"Communication Strategy in Outsourcing Projects","/blog/software-development/5-steps-to-implement-an-effective-communication-strategy-in-outsourcing-software-development-project","blog/software-development/5-steps-to-implement-an-effective-communication-strategy-in-outsourcing-software-development-project",{"title":334,"path":335,"stem":336},"7 Best Practices for Outsourcing Software Development","/blog/software-development/7-best-practices-for-outsourcing-software-development","blog/software-development/7-best-practices-for-outsourcing-software-development",{"title":338,"path":339,"stem":340},"9 Reasons Why Saleor.io Is Best for eCommerce","/blog/software-development/9-reasons-why-the-saleor-io-platform-is-the-best-choice-for-your-ecommerce-website","blog/software-development/9-reasons-why-the-saleor-io-platform-is-the-best-choice-for-your-ecommerce-website",{"title":342,"path":343,"stem":344},"A Look at Bravelab.io’s Clutch 2021 Year In Review","/blog/software-development/a-look-at-bravelab-ios-clutch-2021-year-in-review","blog/software-development/a-look-at-bravelab-ios-clutch-2021-year-in-review",{"title":346,"path":347,"stem":348},"A quick introduction to profit sharing implementation","/blog/software-development/a-quick-introduction-to-profit-sharing-implementation","blog/software-development/a-quick-introduction-to-profit-sharing-implementation",{"title":350,"path":351,"stem":352},"AI Audio Similarity Search: The Future of Sound Library Discovery","/blog/software-development/ai-audio-similarity-search-for-sound-libraries","blog/software-development/ai-audio-similarity-search-for-sound-libraries",{"title":354,"path":355,"stem":356},"Automate Repetitive Tasks for Better Results","/blog/software-development/automate-repetitive-tasks-to-improve-your-business-performance","blog/software-development/automate-repetitive-tasks-to-improve-your-business-performance",{"title":358,"path":359,"stem":360},"Automating Success: The Art of Unified Documentation","/blog/software-development/automating-success-the-art-of-unified-documentation","blog/software-development/automating-success-the-art-of-unified-documentation",{"title":362,"path":363,"stem":364},"Brave 3.0 Website Redesign, Part 2: Solution","/blog/software-development/brave-3-0-how-we-conducted-website-redesign-part-2-solution","blog/software-development/brave-3-0-how-we-conducted-website-redesign-part-2-solution",{"title":366,"path":367,"stem":368},"Brave 3.0, Part 4: Tech Stack and Recap","/blog/software-development/brave-3-0-part-4-technologies-behind-and-final-series-recap","blog/software-development/brave-3-0-part-4-technologies-behind-and-final-series-recap",{"title":370,"path":371,"stem":372},"Brave 3.0 – redesign process part 1. The Challenge","/blog/software-development/brave-3-0-redesign-process-part-1-challenge","blog/software-development/brave-3-0-redesign-process-part-1-challenge",{"title":374,"path":375,"stem":376},"Brave 3.0 – redesign process, part 3. Lesson learned","/blog/software-development/brave-3-0-redesign-process-part-3-lesson-learned","blog/software-development/brave-3-0-redesign-process-part-3-lesson-learned",{"title":378,"path":379,"stem":380},"Bravelab.io: Top Software Developer by Clutch","/blog/software-development/bravelab-io-is-recognized-as-a-top-custom-software-developer-by-clutch","blog/software-development/bravelab-io-is-recognized-as-a-top-custom-software-developer-by-clutch",{"title":382,"path":383,"stem":384},"Bravelab.io: Top Developer in Poland by Clutch","/blog/software-development/bravelab-io-named-top-software-developer-in-poland-by-clutch","blog/software-development/bravelab-io-named-top-software-developer-in-poland-by-clutch",{"title":386,"path":387,"stem":388},"MusicTech Lab Partners with LALAL.AI","/blog/software-development/bravelab-partners-with-the-audio-lalal-ai","blog/software-development/bravelab-partners-with-the-audio-lalal-ai",{"title":390,"path":391,"stem":392},"MusicTech Lab Partners with The Audio Programmer","/blog/software-development/bravelab-partners-with-the-audio-programmer","blog/software-development/bravelab-partners-with-the-audio-programmer",{"title":394,"path":395,"stem":396},"Bravelab's team about productivity","/blog/software-development/bravelabs-team-about-productivity","blog/software-development/bravelabs-team-about-productivity",{"title":398,"path":399,"stem":400},"Braveloper","/blog/software-development/braveloper","blog/software-development/braveloper",{"title":402,"path":403,"stem":404},"Bravely App: Boost Productivity with Django","/blog/software-development/bravely-app-how-to-be-more-productive-with-django-quick","blog/software-development/bravely-app-how-to-be-more-productive-with-django-quick",{"title":406,"path":407,"stem":408},"DIY MIDI Controller for Ableton with Arduino","/blog/software-development/building-a-diy-midi-controller-for-ableton-live-with-arduino","blog/software-development/building-a-diy-midi-controller-for-ableton-live-with-arduino",{"title":410,"path":411,"stem":412},"C2PA in Ableton: Making AI Music Provenance Visible Inside Your DAW","/blog/software-development/c2pa-in-ableton-max-for-live","blog/software-development/c2pa-in-ableton-max-for-live",{"title":414,"path":415,"stem":416},"Change Detection mechanism in Angular","/blog/software-development/change-detection-mechanism-in-angular","blog/software-development/change-detection-mechanism-in-angular",{"title":418,"path":419,"stem":420},"Communication Channels in Remote Work","/blog/software-development/comparison-of-the-communication-channels-in-remote-work","blog/software-development/comparison-of-the-communication-channels-in-remote-work",{"title":422,"path":423,"stem":424},"Connecting Your Max for Live Device to a Cloud API","/blog/software-development/connecting-your-max-for-live-device-to-a-cloud-api","blog/software-development/connecting-your-max-for-live-device-to-a-cloud-api",{"title":426,"path":427,"stem":428},"From Voice Memo to Studio: The Cross-Platform Problem for Creators","/blog/software-development/cross-platform-problem-for-creators","blog/software-development/cross-platform-problem-for-creators",{"title":430,"path":431,"stem":432},"Cultural transformation through the pandemic era","/blog/software-development/cultural-transformation-through-the-pandemic-era","blog/software-development/cultural-transformation-through-the-pandemic-era",{"title":434,"path":435,"stem":436},"D-Commerce Decoded: Cutting Through the Hype","/blog/software-development/d-commerce-decoded-cutting-through-the-hype","blog/software-development/d-commerce-decoded-cutting-through-the-hype",{"title":438,"path":439,"stem":440},"Dev Meeting 002: Intro to DDD","/blog/software-development/dev-meeting-002-introduction-to-domain-driven-design-ddd","blog/software-development/dev-meeting-002-introduction-to-domain-driven-design-ddd",{"title":442,"path":443,"stem":444},"Dev Meeting 003: Web3 Primer","/blog/software-development/dev-meeting-003-web3-primer","blog/software-development/dev-meeting-003-web3-primer",{"title":446,"path":447,"stem":448},"Dev Meeting 004: Introduction to Event Storming","/blog/software-development/dev-meeting-004-introduction-to-event-storming","blog/software-development/dev-meeting-004-introduction-to-event-storming",{"title":450,"path":451,"stem":452},"Dev Meeting 001: Kubernetes is a Framework","/blog/software-development/dev-meeting-kubernetes-is-a-framework","blog/software-development/dev-meeting-kubernetes-is-a-framework",{"title":454,"path":455,"stem":456},"Did You Know? 10 Developer Tips from Real Codebases","/blog/software-development/did-you-know-dev-tips-part-1","blog/software-development/did-you-know-dev-tips-part-1",{"title":458,"path":459,"stem":460},"10 Surprising MusicTech Facts (Part 2)","/blog/software-development/did-you-know-musictech-facts-part-2","blog/software-development/did-you-know-musictech-facts-part-2",{"title":462,"path":463,"stem":464},"Django-cms and GraphQL","/blog/software-development/django-cms-and-graphql","blog/software-development/django-cms-and-graphql",{"title":466,"path":467,"stem":468},"Does Zappa make it super easy?","/blog/software-development/does-zappa-make-it-super-easy","blog/software-development/does-zappa-make-it-super-easy",{"title":470,"path":471,"stem":472},"Establishing cooperation between Netlify and Bravelab","/blog/software-development/establishing-cooperation-between-netlify-and-bravelab","blog/software-development/establishing-cooperation-between-netlify-and-bravelab",{"title":474,"path":475,"stem":476},"Export Ableton Locators to JSON via Max for Live","/blog/software-development/exporting-ableton-live-locators-to-json-with-max-for-live","blog/software-development/exporting-ableton-live-locators-to-json-with-max-for-live",{"title":478,"path":479,"stem":480},"IT Outsourcing: Success and Failure Factors","/blog/software-development/factors-that-contribute-to-the-success-or-failure-of-an-it-outsourcing-project","blog/software-development/factors-that-contribute-to-the-success-or-failure-of-an-it-outsourcing-project",{"title":482,"path":483,"stem":484},"Flutter 2022 Strategy: Analyzing the Roadmap","/blog/software-development/flutter-strategy-for-2022-analyzing-the-new-flutter-roadmap","blog/software-development/flutter-strategy-for-2022-analyzing-the-new-flutter-roadmap",{"title":486,"path":487,"stem":488},"Git Better #1 — Commit Message Convention","/blog/software-development/git-better-1-see-more-with-a-commit-message-convention","blog/software-development/git-better-1-see-more-with-a-commit-message-convention",{"title":490,"path":491,"stem":492},"Hasura in action. How to use it with Django","/blog/software-development/hasura-in-action","blog/software-development/hasura-in-action",{"title":494,"path":495,"stem":496},"Holacracy why and where we are","/blog/software-development/holacracy-why-and-where-we-are","blog/software-development/holacracy-why-and-where-we-are",{"title":498,"path":499,"stem":500},"How does JavaScript work","/blog/software-development/how-does-javascript-work","blog/software-development/how-does-javascript-work",{"title":502,"path":503,"stem":504},"How important is good UX/UI design?","/blog/software-development/how-important-is-good-ux-ui-design","blog/software-development/how-important-is-good-ux-ui-design",{"title":506,"path":507,"stem":508},"How repetitive tasks impact your business","/blog/software-development/how-repetitive-tasks-impact-your-business","blog/software-development/how-repetitive-tasks-impact-your-business",{"title":510,"path":511,"stem":512},"Becoming a Vue.js Dev: Do Paid Trials Work?","/blog/software-development/how-to-become-a-vue-js-developer-and-whether-paid-trials-in-it-work-out","blog/software-development/how-to-become-a-vue-js-developer-and-whether-paid-trials-in-it-work-out",{"title":514,"path":515,"stem":516},"How to Build an MVP in 6 Steps","/blog/software-development/how-to-build-a-minimum-viable-product-mvp-in-6-steps","blog/software-development/how-to-build-a-minimum-viable-product-mvp-in-6-steps",{"title":518,"path":519,"stem":520},"How to conduct workshops for creative industry?","/blog/software-development/how-to-conduct-workshops-for-creative-industry","blog/software-development/how-to-conduct-workshops-for-creative-industry",{"title":522,"path":523,"stem":524},"How to easily create form in Angular","/blog/software-development/how-to-easily-create-form-in-angular","blog/software-development/how-to-easily-create-form-in-angular",{"title":526,"path":527,"stem":528},"How to export orders in Saleor.io to XLSX file","/blog/software-development/how-to-export-orders-in-saleor-io-to-xlsx-file","blog/software-development/how-to-export-orders-in-saleor-io-to-xlsx-file",{"title":530,"path":531,"stem":532},"Handling High Loads on E-Commerce Platforms","/blog/software-development/how-to-handle-high-loads-on-e-commerce-platform-with-ease","blog/software-development/how-to-handle-high-loads-on-e-commerce-platform-with-ease",{"title":534,"path":535,"stem":536},"How to launch Saleor.io shop instance within 40h","/blog/software-development/how-to-launch-saleor-io-shop-instance-within-40h","blog/software-development/how-to-launch-saleor-io-shop-instance-within-40h",{"title":538,"path":539,"stem":540},"First Steps to Build a Business Relationship","/blog/software-development/how-to-make-the-first-step-to-establish-a-business-relationship","blog/software-development/how-to-make-the-first-step-to-establish-a-business-relationship",{"title":542,"path":543,"stem":544},"Multi-Tenant Apps with Django and Saleor.io","/blog/software-development/how-to-manage-tenants-in-the-multitenant-app-based-on-django-tenants-and-saleor-io-platform","blog/software-development/how-to-manage-tenants-in-the-multitenant-app-based-on-django-tenants-and-saleor-io-platform",{"title":546,"path":547,"stem":548},"Notion Backup Tool Built in 3 Days with Python","/blog/software-development/how-we-built-a-notion-backup-tool-in-3-days-with-pythonvue-and-why","blog/software-development/how-we-built-a-notion-backup-tool-in-3-days-with-pythonvue-and-why",{"title":550,"path":551,"stem":552},"Important new features in Python 3.8","/blog/software-development/important-new-features-in-python-3-8","blog/software-development/important-new-features-in-python-3-8",{"title":554,"path":555,"stem":556},"Installing Proxmox on dedicated server from OVH","/blog/software-development/installing-proxmox-on-dedicated-server-from-ovh","blog/software-development/installing-proxmox-on-dedicated-server-from-ovh",{"title":558,"path":559,"stem":560},"Integrating SignNow E-Signatures into Your Django Application","/blog/software-development/integrating-signnow-e-signatures-into-your-django-application","blog/software-development/integrating-signnow-e-signatures-into-your-django-application",{"title":562,"path":563,"stem":564},"Tempus Metronome and GetSongBPM API","/blog/software-development/integrating-tempus-metronome-with-the-getsongbpm-api-what-bpm-really-means-and-how-to-use-it","blog/software-development/integrating-tempus-metronome-with-the-getsongbpm-api-what-bpm-really-means-and-how-to-use-it",{"title":566,"path":567,"stem":568},"Introducing MusicTech Poland","/blog/software-development/introducing-musictech-poland","blog/software-development/introducing-musictech-poland",{"title":570,"path":571,"stem":572},"Vue.js as a Frontend for Saleor.io","/blog/software-development/is-it-possible-to-use-vue-js-as-a-frontend-for-saleor-io-platform","blog/software-development/is-it-possible-to-use-vue-js-as-a-frontend-for-saleor-io-platform",{"title":574,"path":575,"stem":576},"Is your business ready for the cashless era?","/blog/software-development/is-your-business-ready-for-the-cashless-era","blog/software-development/is-your-business-ready-for-the-cashless-era",{"title":578,"path":579,"stem":580},"Is your face ready to buy?","/blog/software-development/is-your-face-ready-to-buy","blog/software-development/is-your-face-ready-to-buy",{"title":582,"path":583,"stem":584},"JS Frameworks: Trends and Opportunities","/blog/software-development/javascript-trending-frameworks-and-market-opportunities","blog/software-development/javascript-trending-frameworks-and-market-opportunities",{"title":586,"path":587,"stem":588},"Kanban Board: Boost Your Team Productivity","/blog/software-development/kanban-board-methodology-hack-your-companys-productivity","blog/software-development/kanban-board-methodology-hack-your-companys-productivity",{"title":590,"path":591,"stem":592},"Verified Human Cert MCP Server: Prove Your Music Is Human-Made, Right from the Terminal","/blog/software-development/mcp-verified-human-cert-open-source","blog/software-development/mcp-verified-human-cert-open-source",{"title":594,"path":595,"stem":596},"Migrating from TravisCI to Github Actions","/blog/software-development/migrating-from-travisci-to-github-actions","blog/software-development/migrating-from-travisci-to-github-actions",{"title":598,"path":599,"stem":600},"MusicTech Lab: Top Software Developer by Clutch","/blog/software-development/musictechlab-is-recognized-as-a-top-custom-software-developer-by-clutch","blog/software-development/musictechlab-is-recognized-as-a-top-custom-software-developer-by-clutch",{"title":602,"path":603,"stem":604},"MusicTech Lab x Verified Human: Building a Trust Layer for Human-Made Music","/blog/software-development/musictechlab_blog_verified_human_partnership","blog/software-development/musictechlab_blog_verified_human_partnership",{"title":606,"path":607,"stem":608},"MusicXML: Standard for Music Notation","/blog/software-development/musicxml-standard-for-music-notation-and-education","blog/software-development/musicxml-standard-for-music-notation-and-education",{"title":610,"path":611,"stem":612},"Only a few books but dozens of ideas","/blog/software-development/only-a-few-books-but-dozens-of-ideas","blog/software-development/only-a-few-books-but-dozens-of-ideas",{"title":614,"path":615,"stem":616},"Overdue Invoices and Issue Tracker Integration","/blog/software-development/overdue-invoices-integration-with-the-issue-tracking-system","blog/software-development/overdue-invoices-integration-with-the-issue-tracking-system",{"title":618,"path":619,"stem":620},"Performing SAML SSO using JWT in Django","/blog/software-development/performing-saml-sso-using-jwt-in-django","blog/software-development/performing-saml-sso-using-jwt-in-django",{"title":622,"path":623,"stem":624},"Progressive Web Apps for Mobile Development","/blog/software-development/progressive-web-apps-a-new-way-of-creating-mobile-application","blog/software-development/progressive-web-apps-a-new-way-of-creating-mobile-application",{"title":626,"path":627,"stem":628},"Recruitment System: Gmail, Jira, and CRM","/blog/software-development/recruitment-system-integrating-gmail-bravely-jira-slack-and-copper-crm","blog/software-development/recruitment-system-integrating-gmail-bravely-jira-slack-and-copper-crm",{"title":630,"path":631,"stem":632},"Scratch Me: Chrome Extension for Leads","/blog/software-development/scratch-me-a-simple-chrome-extension-which-will-increase-your-productivity","blog/software-development/scratch-me-a-simple-chrome-extension-which-will-increase-your-productivity",{"title":634,"path":635,"stem":636},"Scratch Me – integration with the Copper CRM","/blog/software-development/scratch-me-integration-with-the-copper-crm","blog/software-development/scratch-me-integration-with-the-copper-crm",{"title":638,"path":639,"stem":640},"SignNow MCP Server: E-Signatures Straight from Claude Code","/blog/software-development/signnow-mcp-server-e-signatures-from-claude-code","blog/software-development/signnow-mcp-server-e-signatures-from-claude-code",{"title":642,"path":643,"stem":644},"Music Industry Tech Openings (March 2024 Update)","/blog/software-development/technical-job-opportunities-in-the-music-industry","blog/software-development/technical-job-opportunities-in-the-music-industry",{"title":646,"path":647,"stem":648},"Thanks app – a Management 3.0 solution","/blog/software-development/thanks-app-a-management-3-0-solution","blog/software-development/thanks-app-a-management-3-0-solution",{"title":650,"path":651,"stem":652},"Colonial Pipeline Case: 7 Security Reminders","/blog/software-development/the-case-of-colonial-pipeline-and-7-security-reminders","blog/software-development/the-case-of-colonial-pipeline-and-7-security-reminders",{"title":654,"path":655,"stem":656},"The Evolution and Future of E-commerce Platforms","/blog/software-development/the-evolution-and-future-of-e-commerce-platforms","blog/software-development/the-evolution-and-future-of-e-commerce-platforms",{"title":658,"path":659,"stem":660},"The Gender Gap in the Tech Industry","/blog/software-development/the-gender-gap-in-the-tech-industry","blog/software-development/the-gender-gap-in-the-tech-industry",{"title":662,"path":663,"stem":664},"First Attempt to Implement 4DX at Bravelab.io","/blog/software-development/the-very-first-attempt-to-implement-4dx-in-bravelab-io","blog/software-development/the-very-first-attempt-to-implement-4dx-in-bravelab-io",{"title":666,"path":667,"stem":668},"The WTF Scale: IT Project Complexity","/blog/software-development/the-wtf-programming-scale-measuring-it-project-complexity","blog/software-development/the-wtf-programming-scale-measuring-it-project-complexity",{"title":670,"path":671,"stem":672},"Top 10 articles through the eyes of our developers","/blog/software-development/top-10-articles-through-the-eyes-of-our-developers","blog/software-development/top-10-articles-through-the-eyes-of-our-developers",{"title":674,"path":675,"stem":676},"Top 6 apps made with Flutter","/blog/software-development/top-6-apps-made-with-flutter","blog/software-development/top-6-apps-made-with-flutter",{"title":678,"path":679,"stem":680},"Uber 101: How Uber Made It to the Top","/blog/software-development/uber-101-how-this-ride-sharing-behemoth-made-it-to-the-top","blog/software-development/uber-101-how-this-ride-sharing-behemoth-made-it-to-the-top",{"title":682,"path":683,"stem":684},"MusicTech Lab Partners with Music Glue","/blog/software-development/unifying-artists-and-audiences-exploring-music-glue","blog/software-development/unifying-artists-and-audiences-exploring-music-glue",{"title":686,"path":687,"stem":688},"Why AI Will Defeat Traditional HR","/blog/software-development/warning-why-artificial-intelligence-will-defeat-traditional-hr","blog/software-development/warning-why-artificial-intelligence-will-defeat-traditional-hr",{"title":690,"path":691,"stem":692},"What is a Discovery Document?","/blog/software-development/what-is-discovery-document","blog/software-development/what-is-discovery-document",{"title":694,"path":695,"stem":696},"What is Flutter, and Why is it Worth Considering?","/blog/software-development/what-is-flutter-and-why-is-it-worth-considering","blog/software-development/what-is-flutter-and-why-is-it-worth-considering",{"title":698,"path":699,"stem":700},"What is a Watermarked Song?","/blog/software-development/what-is-watermarked-song","blog/software-development/what-is-watermarked-song",{"title":702,"path":703,"stem":704},"Choosing a Frontend Framework for the Web","/blog/software-development/which-framework-should-you-choose-for-the-frontend-web-platform-development","blog/software-development/which-framework-should-you-choose-for-the-frontend-web-platform-development",{"title":706,"path":707,"stem":708},"Why DAWs Are the Wrong Tool for Starting a Song","/blog/software-development/why-daws-wrong-tool-for-starting-song","blog/software-development/why-daws-wrong-tool-for-starting-song",{"title":710,"path":711,"stem":712},"Why the Programming World Loves Python","/blog/software-development/why-the-programming-world-loves-python","blog/software-development/why-the-programming-world-loves-python",{"title":714,"path":715,"stem":716},"Why We Don't Build Chat From Scratch (And Neither Should You)","/blog/software-development/why-we-dont-build-chat-from-scratch","blog/software-development/why-we-dont-build-chat-from-scratch",{"title":718,"path":719,"stem":720},"Why we use Sanity.io","/blog/software-development/why-we-use-sanity-io","blog/software-development/why-we-use-sanity-io",{"title":722,"path":723,"stem":724,"children":725,"page":69},"Sportstech","/blog/sportstech","blog/sportstech",[726,730],{"title":727,"path":728,"stem":729},"BeatBuddy Replay: Video Analysis App Challenges","/blog/sportstech/beatbuddy-replay-video-analysis-app-for-swimmers-flutter","blog/sportstech/beatbuddy-replay-video-analysis-app-for-swimmers-flutter",{"title":731,"path":732,"stem":733},"How to Create a Watch Face App for Garmin Watch","/blog/sportstech/how-to-create-watch-face-app-for-garmin-watch","blog/sportstech/how-to-create-watch-face-app-for-garmin-watch",{"id":735,"title":128,"authors":736,"badge":741,"body":742,"category":787,"client":741,"date":788,"description":789,"extension":790,"faq":741,"featured":69,"featuredOrder":741,"hidden":69,"image":791,"keyTakeaways":793,"meta":805,"navigation":794,"path":129,"seo":806,"status":741,"stem":130,"tags":807,"teaser":741,"__hash__":810},"posts/blog/music-data/data-modeling-in-mongodb-with-the-usage-of-design-patterns.md",[737],{"name":738,"avatar":739},"Bartosz Wilk",{"src":740},"/images/people/bartosz-wilk.webp",null,{"type":743,"value":744,"toc":781},"minimark",[745,749,754,757,760,763,767,778],[746,747,748],"p",{},"Using NoSQL databases is slightly different than using primary relational databases like PostgreSQL. You have more freedom in data modeling of your documents cause each document of a collection can contain diversified properties. In larger projects, this advantage can lead to making a little mess in your data. Such as in the programming case, design patterns can help you organize your relations between documents and collections. There are many patterns that you can choose during data modeling in MongoDB, but in this article, you get to know about three of them.",[750,751,753],"h2",{"id":752},"attribute-pattern","Attribute Pattern",[746,755,756],{},"When you have many documents mostly with the same properties, but they also have their specific attributes, this pattern can be useful for you. All you need to do is create one array property for all these characteristics and put them into as key-value objects. This solution will enable you to sort and search documents in an easier way and it will improve the performance. You can use it, eg., in an e-commerce project where you have a lot of similar products.",[746,758,759],{},"This kind of pattern, as the name suggests, makes a tree of dependencies between documents of a collection. There are several approaches to make a tree, but the easiest is to add properties with references to a parent and children of a document. This design pattern will be useful everywhere where there is a hierarchical data structure, eg., dependencies between employees in an organization.",[746,761,762],{},"Last but not least, the design pattern can improve the performance of your application if you often make JOIN operations on your documents. To reduce them, you need to embed the most necessary properties of a document of the second collection into a document of the first one. In this way, you won't have to make JOIN operations so often, and if you need more properties of the document of the second collection, you always have access to take them.",[750,764,766],{"id":765},"summary","Summary",[746,768,769,770,777],{},"To sum up, these three patterns, I described shortly, can improve the organization of your data structure and optimize the performance of your application. I encourage you to read the MongoDB documentation to get to know more. If you want to explore more design patterns, I recommend you to join the data modeling course on ",[771,772,776],"a",{"href":773,"rel":774},"https://university.mongodb.com/",[775],"nofollow","MongoDB university",".",[746,779,780],{},"‍",{"title":782,"searchDepth":783,"depth":783,"links":784},"",2,[785,786],{"id":752,"depth":783,"text":753},{"id":765,"depth":783,"text":766},"music-data","2020-07-22T00:00:00.000Z","The most useful data modeling design patterns in MongoDB are attributed, three, and extended reference patterns.","md",{"src":792},"/images/blog/musictechlab_blog_data-modeling-in-mongodb-with-the-usage-of-design-patterns.webp",{"enabled":794,"items":795},true,[796,799,802],{"text":797,"icon":798},"The Attribute Pattern stores varying properties as key-value arrays for easier search and indexing.","i-lucide-database",{"text":800,"icon":801},"The Tree Pattern creates parent-child references for hierarchical data structures.","i-lucide-layers",{"text":803,"icon":804},"The Extended Reference Pattern embeds frequently-joined fields to reduce costly JOIN operations.","i-lucide-zap",{},{"title":128,"description":789},[808,787,809],"development","mongodb","6bbWNGiAL98eVqA9EG8Vhg2CJRcmJORV5LTnvS_9YiI",[812,814],{"title":124,"path":125,"stem":126,"description":813,"children":-1},"C2PA is the open standard for content provenance, and it just landed in music files. We open-sourced an MCP server so you can read these manifests inside Claude Code.",{"title":132,"path":133,"stem":134,"description":815,"children":-1},"Free DDEX office hours for MusicTech teams. Get help with DDEX compliance, ERN implementation, data models, and integrations with DSPs.",[817,2057,4369,4392],{"id":818,"title":156,"authors":819,"badge":741,"body":824,"category":787,"client":741,"date":2038,"description":2039,"extension":790,"faq":741,"featured":69,"featuredOrder":741,"hidden":69,"image":2040,"keyTakeaways":2042,"meta":2052,"navigation":794,"path":157,"seo":2053,"status":741,"stem":158,"tags":2054,"teaser":741,"__hash__":2056,"score":966},"posts/blog/music-data/hybrid-database-model-in-django-as-a-performance-booster.md",[820],{"name":821,"avatar":822},"Oleksandr Gorbunov",{"src":823},"/images/people/oleksandr-gorbunov.webp",{"type":743,"value":825,"toc":2027},[826,830,833,836,839,843,846,857,862,1154,1161,1165,1336,1339,1343,1768,1775,1779,1969,1972,1976,1985,2003,2013,2017,2020,2023],[750,827,829],{"id":828},"introduction","Introduction",[746,831,832],{},"Handling a high volume of requests per second while managing an extensive product database is a key challenge in modern e-commerce platforms. In this post, I'll show the solutions we implemented in our \"catalog\" microservice to address these challenges.",[746,834,835],{},"We initially built the service in Django — driven by the need for rapid development and Django's convenient admin interface. But as the platform scaled, storing everything in a relational database with multiple joins became a bottleneck.",[746,837,838],{},"Our idea: combine the strengths of both relational and non-relational databases. Keep the relational integrity needed for certain data, while using MongoDB for fast reads on product fields. And keep everything compatible with Django models, Django Admin, and Django REST Framework.",[750,840,842],{"id":841},"solution","Solution",[746,844,845],{},"The goal is to link each Django model object with a MongoDB document. When reading, the model is automatically populated from MongoDB. When writing, changes are synced back to the MongoDB document.",[746,847,848,849,853,854,777],{},"We achieve this by dynamically creating ",[850,851,852],"strong",{},"descriptors"," within the Django model. These descriptors act as proxies to MongoDB fields and are generated using a ",[850,855,856],{},"metaclass",[858,859,861],"h3",{"id":860},"step-1-the-mongodb-field-descriptor","Step 1: The MongoDB Field Descriptor",[863,864,868],"pre",{"className":865,"code":866,"language":867,"meta":782,"style":782},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","class MongoFieldDescriptor:\n    def __init__(self, field_name):\n        self.field_name = field_name\n        self.private_name = f\"_m_{field_name}\"\n\n    def __get__(self, obj, objtype=None):\n        if obj is None:\n            return self\n        # Read directly from the MongoDB document\n        if obj._mongo_document:\n            return obj._mongo_document.get(self.field_name)\n        return getattr(obj, self.private_name, None)\n\n    def __set__(self, obj, value):\n        # Store in private field for batch saving later\n        setattr(obj, self.private_name, value)\n","python",[869,870,871,888,914,933,964,970,995,1011,1020,1027,1041,1068,1096,1101,1124,1130],"code",{"__ignoreMap":782},[872,873,876,880,884],"span",{"class":874,"line":875},"line",1,[872,877,879],{"class":878},"spNyl","class",[872,881,883],{"class":882},"sBMFI"," MongoFieldDescriptor",[872,885,887],{"class":886},"sMK4o",":\n",[872,889,890,893,897,900,904,907,911],{"class":874,"line":783},[872,891,892],{"class":878},"    def",[872,894,896],{"class":895},"s2Zo4"," __init__",[872,898,899],{"class":886},"(",[872,901,903],{"class":902},"s5tWE","self",[872,905,906],{"class":886},",",[872,908,910],{"class":909},"sHdIc"," field_name",[872,912,913],{"class":886},"):\n",[872,915,917,921,923,927,930],{"class":874,"line":916},3,[872,918,920],{"class":919},"sTEyZ","        self",[872,922,777],{"class":886},[872,924,926],{"class":925},"swJcz","field_name",[872,928,929],{"class":886}," =",[872,931,932],{"class":919}," field_name\n",[872,934,936,938,940,943,945,948,952,956,958,961],{"class":874,"line":935},4,[872,937,920],{"class":919},[872,939,777],{"class":886},[872,941,942],{"class":925},"private_name",[872,944,929],{"class":886},[872,946,947],{"class":878}," f",[872,949,951],{"class":950},"sfazB","\"_m_",[872,953,955],{"class":954},"sbssI","{",[872,957,926],{"class":919},[872,959,960],{"class":954},"}",[872,962,963],{"class":950},"\"\n",[872,965,967],{"class":874,"line":966},5,[872,968,969],{"emptyLinePlaceholder":794},"\n",[872,971,973,975,978,980,982,984,987,989,992],{"class":874,"line":972},6,[872,974,892],{"class":878},[872,976,977],{"class":895}," __get__",[872,979,899],{"class":886},[872,981,903],{"class":902},[872,983,906],{"class":886},[872,985,986],{"class":909}," obj",[872,988,906],{"class":886},[872,990,991],{"class":909}," objtype",[872,993,994],{"class":886},"=None):\n",[872,996,998,1002,1005,1008],{"class":874,"line":997},7,[872,999,1001],{"class":1000},"s7zQu","        if",[872,1003,1004],{"class":919}," obj ",[872,1006,1007],{"class":886},"is",[872,1009,1010],{"class":886}," None:\n",[872,1012,1014,1017],{"class":874,"line":1013},8,[872,1015,1016],{"class":1000},"            return",[872,1018,1019],{"class":919}," self\n",[872,1021,1023],{"class":874,"line":1022},9,[872,1024,1026],{"class":1025},"sHwdD","        # Read directly from the MongoDB document\n",[872,1028,1030,1032,1034,1036,1039],{"class":874,"line":1029},10,[872,1031,1001],{"class":1000},[872,1033,986],{"class":919},[872,1035,777],{"class":886},[872,1037,1038],{"class":925},"_mongo_document",[872,1040,887],{"class":886},[872,1042,1044,1046,1048,1050,1052,1054,1057,1059,1061,1063,1065],{"class":874,"line":1043},11,[872,1045,1016],{"class":1000},[872,1047,986],{"class":919},[872,1049,777],{"class":886},[872,1051,1038],{"class":925},[872,1053,777],{"class":886},[872,1055,1056],{"class":895},"get",[872,1058,899],{"class":886},[872,1060,903],{"class":919},[872,1062,777],{"class":886},[872,1064,926],{"class":925},[872,1066,1067],{"class":886},")\n",[872,1069,1071,1074,1077,1079,1082,1084,1087,1089,1091,1093],{"class":874,"line":1070},12,[872,1072,1073],{"class":1000},"        return",[872,1075,1076],{"class":895}," getattr",[872,1078,899],{"class":886},[872,1080,1081],{"class":895},"obj",[872,1083,906],{"class":886},[872,1085,1086],{"class":919}," self",[872,1088,777],{"class":886},[872,1090,942],{"class":925},[872,1092,906],{"class":886},[872,1094,1095],{"class":886}," None)\n",[872,1097,1099],{"class":874,"line":1098},13,[872,1100,969],{"emptyLinePlaceholder":794},[872,1102,1104,1106,1109,1111,1113,1115,1117,1119,1122],{"class":874,"line":1103},14,[872,1105,892],{"class":878},[872,1107,1108],{"class":895}," __set__",[872,1110,899],{"class":886},[872,1112,903],{"class":902},[872,1114,906],{"class":886},[872,1116,986],{"class":909},[872,1118,906],{"class":886},[872,1120,1121],{"class":909}," value",[872,1123,913],{"class":886},[872,1125,1127],{"class":874,"line":1126},15,[872,1128,1129],{"class":1025},"        # Store in private field for batch saving later\n",[872,1131,1133,1136,1138,1140,1142,1144,1146,1148,1150,1152],{"class":874,"line":1132},16,[872,1134,1135],{"class":895},"        setattr",[872,1137,899],{"class":886},[872,1139,1081],{"class":895},[872,1141,906],{"class":886},[872,1143,1086],{"class":919},[872,1145,777],{"class":886},[872,1147,942],{"class":925},[872,1149,906],{"class":886},[872,1151,1121],{"class":895},[872,1153,1067],{"class":886},[746,1155,1156,1157,1160],{},"The descriptor reads fields directly from the MongoDB document. For writes, it stores data in a private ",[869,1158,1159],{},"_m_\u003Cfield>"," attribute so all fields can be saved at once.",[858,1162,1164],{"id":1163},"step-2-the-model-metaclass","Step 2: The Model Metaclass",[863,1166,1168],{"className":865,"code":1167,"language":867,"meta":782,"style":782},"class HybridModelMeta(type(models.Model)):\n    def __new__(mcs, name, bases, namespace):\n        cls = super().__new__(mcs, name, bases, namespace)\n        mongo_fields = getattr(cls, \"mongo_fields\", [])\n        for field_name in mongo_fields:\n            setattr(cls, field_name, MongoFieldDescriptor(field_name))\n        return cls\n",[869,1169,1170,1195,1224,1259,1289,1305,1329],{"__ignoreMap":782},[872,1171,1172,1174,1177,1179,1182,1184,1187,1189,1192],{"class":874,"line":875},[872,1173,879],{"class":878},[872,1175,1176],{"class":882}," HybridModelMeta",[872,1178,899],{"class":886},[872,1180,1181],{"class":882},"type",[872,1183,899],{"class":886},[872,1185,1186],{"class":895},"models",[872,1188,777],{"class":886},[872,1190,1191],{"class":925},"Model",[872,1193,1194],{"class":886},")):\n",[872,1196,1197,1199,1202,1204,1207,1209,1212,1214,1217,1219,1222],{"class":874,"line":783},[872,1198,892],{"class":878},[872,1200,1201],{"class":895}," __new__",[872,1203,899],{"class":886},[872,1205,1206],{"class":909},"mcs",[872,1208,906],{"class":886},[872,1210,1211],{"class":909}," name",[872,1213,906],{"class":886},[872,1215,1216],{"class":909}," bases",[872,1218,906],{"class":886},[872,1220,1221],{"class":909}," namespace",[872,1223,913],{"class":886},[872,1225,1226,1229,1232,1235,1238,1241,1243,1245,1247,1249,1251,1253,1255,1257],{"class":874,"line":916},[872,1227,1228],{"class":919},"        cls ",[872,1230,1231],{"class":886},"=",[872,1233,1234],{"class":882}," super",[872,1236,1237],{"class":886},"().",[872,1239,1240],{"class":895},"__new__",[872,1242,899],{"class":886},[872,1244,1206],{"class":895},[872,1246,906],{"class":886},[872,1248,1211],{"class":895},[872,1250,906],{"class":886},[872,1252,1216],{"class":895},[872,1254,906],{"class":886},[872,1256,1221],{"class":895},[872,1258,1067],{"class":886},[872,1260,1261,1264,1266,1268,1270,1273,1275,1278,1281,1284,1286],{"class":874,"line":935},[872,1262,1263],{"class":919},"        mongo_fields ",[872,1265,1231],{"class":886},[872,1267,1076],{"class":895},[872,1269,899],{"class":886},[872,1271,1272],{"class":919},"cls",[872,1274,906],{"class":886},[872,1276,1277],{"class":886}," \"",[872,1279,1280],{"class":950},"mongo_fields",[872,1282,1283],{"class":886},"\"",[872,1285,906],{"class":886},[872,1287,1288],{"class":886}," [])\n",[872,1290,1291,1294,1297,1300,1303],{"class":874,"line":966},[872,1292,1293],{"class":1000},"        for",[872,1295,1296],{"class":919}," field_name ",[872,1298,1299],{"class":1000},"in",[872,1301,1302],{"class":919}," mongo_fields",[872,1304,887],{"class":886},[872,1306,1307,1310,1312,1314,1316,1318,1320,1322,1324,1326],{"class":874,"line":972},[872,1308,1309],{"class":895},"            setattr",[872,1311,899],{"class":886},[872,1313,1272],{"class":919},[872,1315,906],{"class":886},[872,1317,910],{"class":895},[872,1319,906],{"class":886},[872,1321,883],{"class":895},[872,1323,899],{"class":886},[872,1325,926],{"class":895},[872,1327,1328],{"class":886},"))\n",[872,1330,1331,1333],{"class":874,"line":997},[872,1332,1073],{"class":1000},[872,1334,1335],{"class":919}," cls\n",[746,1337,1338],{},"The metaclass iterates through each MongoDB field name and creates a descriptor with that name, attaching it to the model class.",[858,1340,1342],{"id":1341},"step-3-the-base-hybrid-model","Step 3: The Base Hybrid Model",[863,1344,1346],{"className":865,"code":1345,"language":867,"meta":782,"style":782},"class HybridModel(models.Model, metaclass=HybridModelMeta):\n    class Meta:\n        abstract = True\n\n    _mongo_document = None\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        # Connect to the corresponding MongoDB document\n        self._mongo_document = self.get_mongo_collection().find_one(\n            {\"_id\": str(self.pk)}\n        )\n\n    def save(self, *args, **kwargs):\n        super().save(*args, **kwargs)\n        # Sync _m_ prefixed fields back to MongoDB\n        update_fields = {}\n        for field_name in self.mongo_fields:\n            private = f\"_m_{field_name}\"\n            if hasattr(self, private):\n                update_fields[field_name] = getattr(self, private)\n        if update_fields:\n            self.get_mongo_collection().update_one(\n                {\"_id\": str(self.pk)},\n                {\"$set\": update_fields},\n                upsert=True,\n            )\n",[869,1347,1348,1375,1385,1395,1399,1409,1413,1441,1464,1469,1494,1524,1529,1533,1558,1579,1584,1595,1612,1632,1652,1680,1690,1707,1734,1753,1762],{"__ignoreMap":782},[872,1349,1350,1352,1355,1357,1359,1361,1363,1365,1368,1370,1373],{"class":874,"line":875},[872,1351,879],{"class":878},[872,1353,1354],{"class":882}," HybridModel",[872,1356,899],{"class":886},[872,1358,1186],{"class":882},[872,1360,777],{"class":886},[872,1362,1191],{"class":882},[872,1364,906],{"class":886},[872,1366,1367],{"class":882}," metaclass",[872,1369,1231],{"class":886},[872,1371,1372],{"class":882},"HybridModelMeta",[872,1374,913],{"class":886},[872,1376,1377,1380,1383],{"class":874,"line":783},[872,1378,1379],{"class":878},"    class",[872,1381,1382],{"class":882}," Meta",[872,1384,887],{"class":886},[872,1386,1387,1390,1392],{"class":874,"line":916},[872,1388,1389],{"class":919},"        abstract ",[872,1391,1231],{"class":886},[872,1393,1394],{"class":886}," True\n",[872,1396,1397],{"class":874,"line":935},[872,1398,969],{"emptyLinePlaceholder":794},[872,1400,1401,1404,1406],{"class":874,"line":966},[872,1402,1403],{"class":919},"    _mongo_document ",[872,1405,1231],{"class":886},[872,1407,1408],{"class":886}," None\n",[872,1410,1411],{"class":874,"line":972},[872,1412,969],{"emptyLinePlaceholder":794},[872,1414,1415,1417,1419,1421,1423,1425,1428,1431,1433,1436,1439],{"class":874,"line":997},[872,1416,892],{"class":878},[872,1418,896],{"class":895},[872,1420,899],{"class":886},[872,1422,903],{"class":902},[872,1424,906],{"class":886},[872,1426,1427],{"class":886}," *",[872,1429,1430],{"class":909},"args",[872,1432,906],{"class":886},[872,1434,1435],{"class":886}," **",[872,1437,1438],{"class":909},"kwargs",[872,1440,913],{"class":886},[872,1442,1443,1446,1448,1451,1454,1456,1458,1460,1462],{"class":874,"line":1013},[872,1444,1445],{"class":882},"        super",[872,1447,1237],{"class":886},[872,1449,1450],{"class":895},"__init__",[872,1452,1453],{"class":886},"(*",[872,1455,1430],{"class":895},[872,1457,906],{"class":886},[872,1459,1435],{"class":886},[872,1461,1438],{"class":895},[872,1463,1067],{"class":886},[872,1465,1466],{"class":874,"line":1022},[872,1467,1468],{"class":1025},"        # Connect to the corresponding MongoDB document\n",[872,1470,1471,1473,1475,1477,1479,1481,1483,1486,1488,1491],{"class":874,"line":1029},[872,1472,920],{"class":919},[872,1474,777],{"class":886},[872,1476,1038],{"class":925},[872,1478,929],{"class":886},[872,1480,1086],{"class":919},[872,1482,777],{"class":886},[872,1484,1485],{"class":895},"get_mongo_collection",[872,1487,1237],{"class":886},[872,1489,1490],{"class":895},"find_one",[872,1492,1493],{"class":886},"(\n",[872,1495,1496,1499,1501,1504,1506,1509,1512,1514,1516,1518,1521],{"class":874,"line":1043},[872,1497,1498],{"class":886},"            {",[872,1500,1283],{"class":886},[872,1502,1503],{"class":950},"_id",[872,1505,1283],{"class":886},[872,1507,1508],{"class":886},":",[872,1510,1511],{"class":882}," str",[872,1513,899],{"class":886},[872,1515,903],{"class":919},[872,1517,777],{"class":886},[872,1519,1520],{"class":925},"pk",[872,1522,1523],{"class":886},")}\n",[872,1525,1526],{"class":874,"line":1070},[872,1527,1528],{"class":886},"        )\n",[872,1530,1531],{"class":874,"line":1098},[872,1532,969],{"emptyLinePlaceholder":794},[872,1534,1535,1537,1540,1542,1544,1546,1548,1550,1552,1554,1556],{"class":874,"line":1103},[872,1536,892],{"class":878},[872,1538,1539],{"class":895}," save",[872,1541,899],{"class":886},[872,1543,903],{"class":902},[872,1545,906],{"class":886},[872,1547,1427],{"class":886},[872,1549,1430],{"class":909},[872,1551,906],{"class":886},[872,1553,1435],{"class":886},[872,1555,1438],{"class":909},[872,1557,913],{"class":886},[872,1559,1560,1562,1564,1567,1569,1571,1573,1575,1577],{"class":874,"line":1126},[872,1561,1445],{"class":882},[872,1563,1237],{"class":886},[872,1565,1566],{"class":895},"save",[872,1568,1453],{"class":886},[872,1570,1430],{"class":895},[872,1572,906],{"class":886},[872,1574,1435],{"class":886},[872,1576,1438],{"class":895},[872,1578,1067],{"class":886},[872,1580,1581],{"class":874,"line":1132},[872,1582,1583],{"class":1025},"        # Sync _m_ prefixed fields back to MongoDB\n",[872,1585,1587,1590,1592],{"class":874,"line":1586},17,[872,1588,1589],{"class":919},"        update_fields ",[872,1591,1231],{"class":886},[872,1593,1594],{"class":886}," {}\n",[872,1596,1598,1600,1602,1604,1606,1608,1610],{"class":874,"line":1597},18,[872,1599,1293],{"class":1000},[872,1601,1296],{"class":919},[872,1603,1299],{"class":1000},[872,1605,1086],{"class":919},[872,1607,777],{"class":886},[872,1609,1280],{"class":925},[872,1611,887],{"class":886},[872,1613,1615,1618,1620,1622,1624,1626,1628,1630],{"class":874,"line":1614},19,[872,1616,1617],{"class":919},"            private ",[872,1619,1231],{"class":886},[872,1621,947],{"class":878},[872,1623,951],{"class":950},[872,1625,955],{"class":954},[872,1627,926],{"class":919},[872,1629,960],{"class":954},[872,1631,963],{"class":950},[872,1633,1635,1638,1641,1643,1645,1647,1650],{"class":874,"line":1634},20,[872,1636,1637],{"class":1000},"            if",[872,1639,1640],{"class":895}," hasattr",[872,1642,899],{"class":886},[872,1644,903],{"class":919},[872,1646,906],{"class":886},[872,1648,1649],{"class":895}," private",[872,1651,913],{"class":886},[872,1653,1655,1658,1661,1663,1666,1668,1670,1672,1674,1676,1678],{"class":874,"line":1654},21,[872,1656,1657],{"class":919},"                update_fields",[872,1659,1660],{"class":886},"[",[872,1662,926],{"class":919},[872,1664,1665],{"class":886},"]",[872,1667,929],{"class":886},[872,1669,1076],{"class":895},[872,1671,899],{"class":886},[872,1673,903],{"class":919},[872,1675,906],{"class":886},[872,1677,1649],{"class":895},[872,1679,1067],{"class":886},[872,1681,1683,1685,1688],{"class":874,"line":1682},22,[872,1684,1001],{"class":1000},[872,1686,1687],{"class":919}," update_fields",[872,1689,887],{"class":886},[872,1691,1693,1696,1698,1700,1702,1705],{"class":874,"line":1692},23,[872,1694,1695],{"class":919},"            self",[872,1697,777],{"class":886},[872,1699,1485],{"class":895},[872,1701,1237],{"class":886},[872,1703,1704],{"class":895},"update_one",[872,1706,1493],{"class":886},[872,1708,1710,1713,1715,1717,1719,1721,1723,1725,1727,1729,1731],{"class":874,"line":1709},24,[872,1711,1712],{"class":886},"                {",[872,1714,1283],{"class":886},[872,1716,1503],{"class":950},[872,1718,1283],{"class":886},[872,1720,1508],{"class":886},[872,1722,1511],{"class":882},[872,1724,899],{"class":886},[872,1726,903],{"class":919},[872,1728,777],{"class":886},[872,1730,1520],{"class":925},[872,1732,1733],{"class":886},")},\n",[872,1735,1737,1739,1741,1744,1746,1748,1750],{"class":874,"line":1736},25,[872,1738,1712],{"class":886},[872,1740,1283],{"class":886},[872,1742,1743],{"class":950},"$set",[872,1745,1283],{"class":886},[872,1747,1508],{"class":886},[872,1749,1687],{"class":895},[872,1751,1752],{"class":886},"},\n",[872,1754,1756,1759],{"class":874,"line":1755},26,[872,1757,1758],{"class":909},"                upsert",[872,1760,1761],{"class":886},"=True,\n",[872,1763,1765],{"class":874,"line":1764},27,[872,1766,1767],{"class":886},"            )\n",[746,1769,1770,1771,1774],{},"This abstract base class connects a MongoDB document to the model on initialization and automatically syncs ",[869,1772,1773],{},"_m_","-prefixed fields back to MongoDB on save.",[858,1776,1778],{"id":1777},"step-4-the-product-model","Step 4: The Product Model",[863,1780,1782],{"className":865,"code":1781,"language":867,"meta":782,"style":782},"class Product(HybridModel):\n    # Regular Django fields (relational)\n    category = models.ForeignKey(Category, on_delete=models.CASCADE)\n    created_at = models.DateTimeField(auto_now_add=True)\n\n    # Fields stored in MongoDB for fast reads\n    mongo_fields = [\"name\", \"description\", \"price\", \"attributes\", \"images\"]\n\n    @classmethod\n    def get_mongo_collection(cls):\n        return mongo_db[\"products\"]\n",[869,1783,1784,1798,1803,1839,1861,1865,1870,1926,1930,1938,1951],{"__ignoreMap":782},[872,1785,1786,1788,1791,1793,1796],{"class":874,"line":875},[872,1787,879],{"class":878},[872,1789,1790],{"class":882}," Product",[872,1792,899],{"class":886},[872,1794,1795],{"class":882},"HybridModel",[872,1797,913],{"class":886},[872,1799,1800],{"class":874,"line":783},[872,1801,1802],{"class":1025},"    # Regular Django fields (relational)\n",[872,1804,1805,1808,1810,1813,1815,1818,1820,1823,1825,1828,1830,1832,1834,1837],{"class":874,"line":916},[872,1806,1807],{"class":919},"    category ",[872,1809,1231],{"class":886},[872,1811,1812],{"class":919}," models",[872,1814,777],{"class":886},[872,1816,1817],{"class":895},"ForeignKey",[872,1819,899],{"class":886},[872,1821,1822],{"class":895},"Category",[872,1824,906],{"class":886},[872,1826,1827],{"class":909}," on_delete",[872,1829,1231],{"class":886},[872,1831,1186],{"class":895},[872,1833,777],{"class":886},[872,1835,1836],{"class":925},"CASCADE",[872,1838,1067],{"class":886},[872,1840,1841,1844,1846,1848,1850,1853,1855,1858],{"class":874,"line":935},[872,1842,1843],{"class":919},"    created_at ",[872,1845,1231],{"class":886},[872,1847,1812],{"class":919},[872,1849,777],{"class":886},[872,1851,1852],{"class":895},"DateTimeField",[872,1854,899],{"class":886},[872,1856,1857],{"class":909},"auto_now_add",[872,1859,1860],{"class":886},"=True)\n",[872,1862,1863],{"class":874,"line":966},[872,1864,969],{"emptyLinePlaceholder":794},[872,1866,1867],{"class":874,"line":972},[872,1868,1869],{"class":1025},"    # Fields stored in MongoDB for fast reads\n",[872,1871,1872,1875,1877,1880,1882,1885,1887,1889,1891,1894,1896,1898,1900,1903,1905,1907,1909,1912,1914,1916,1918,1921,1923],{"class":874,"line":997},[872,1873,1874],{"class":919},"    mongo_fields ",[872,1876,1231],{"class":886},[872,1878,1879],{"class":886}," [",[872,1881,1283],{"class":886},[872,1883,1884],{"class":950},"name",[872,1886,1283],{"class":886},[872,1888,906],{"class":886},[872,1890,1277],{"class":886},[872,1892,1893],{"class":950},"description",[872,1895,1283],{"class":886},[872,1897,906],{"class":886},[872,1899,1277],{"class":886},[872,1901,1902],{"class":950},"price",[872,1904,1283],{"class":886},[872,1906,906],{"class":886},[872,1908,1277],{"class":886},[872,1910,1911],{"class":950},"attributes",[872,1913,1283],{"class":886},[872,1915,906],{"class":886},[872,1917,1277],{"class":886},[872,1919,1920],{"class":950},"images",[872,1922,1283],{"class":886},[872,1924,1925],{"class":886},"]\n",[872,1927,1928],{"class":874,"line":1013},[872,1929,969],{"emptyLinePlaceholder":794},[872,1931,1932,1935],{"class":874,"line":1022},[872,1933,1934],{"class":886},"    @",[872,1936,1937],{"class":882},"classmethod\n",[872,1939,1940,1942,1945,1947,1949],{"class":874,"line":1029},[872,1941,892],{"class":878},[872,1943,1944],{"class":895}," get_mongo_collection",[872,1946,899],{"class":886},[872,1948,1272],{"class":909},[872,1950,913],{"class":886},[872,1952,1953,1955,1958,1960,1962,1965,1967],{"class":874,"line":1043},[872,1954,1073],{"class":1000},[872,1956,1957],{"class":919}," mongo_db",[872,1959,1660],{"class":886},[872,1961,1283],{"class":886},[872,1963,1964],{"class":950},"products",[872,1966,1283],{"class":886},[872,1968,1925],{"class":886},[746,1970,1971],{},"The implementation is straightforward: define a MongoDB document containing product fields and connect it to the Product model. The model can still use Django relationships normally.",[750,1973,1975],{"id":1974},"performance-comparison","Performance Comparison",[746,1977,1978,1979,1984],{},"We created an endpoint to retrieve products within a specific category and benchmarked it using the ",[771,1980,1983],{"href":1981,"rel":1982},"https://github.com/hatoo/oha",[775],"OHA"," tool — 10,000 requests across 50 threads.",[1986,1987,1994,1999],"div",{"className":1988},[1989,1990,1991,1992,1993],"grid","grid-cols-1","md:grid-cols-2","gap-4","my-8",[1995,1996],"spotlight-card",{"description":1997,"title":1998},"- Avg response time: ~120ms - Requests/sec: ~400 - P99 latency: ~250ms","Standard Django (PostgreSQL only)",[1995,2000],{"description":2001,"title":2002},"- Avg response time: ~14ms - Requests/sec: ~3,500 - P99 latency: ~30ms","Hybrid Model (PostgreSQL + MongoDB)",[2004,2005,2006],"tip",{},[746,2007,2008,2009,2012],{},"The hybrid model showed an ",[850,2010,2011],{},"8–10x speed improvement"," over the standard Django implementation for read-heavy catalog queries.",[750,2014,2016],{"id":2015},"conclusion","Conclusion",[746,2018,2019],{},"The hybrid model approach significantly enhanced performance by offloading read-heavy product data to MongoDB while keeping relational integrity in PostgreSQL. There's room for further improvement — loading all MongoDB fields at once, batch-loading documents for entire querysets, and adding caching.",[746,2021,2022],{},"This approach offers a practical balance between relational and non-relational databases while staying fully compatible with Django Admin and Django REST Framework.",[2024,2025,2026],"style",{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .s5tWE, html code.shiki .s5tWE{--shiki-light:#E53935;--shiki-light-font-style:italic;--shiki-default:#F07178;--shiki-default-font-style:italic;--shiki-dark:#F07178;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":782,"searchDepth":783,"depth":783,"links":2028},[2029,2030,2036,2037],{"id":828,"depth":783,"text":829},{"id":841,"depth":783,"text":842,"children":2031},[2032,2033,2034,2035],{"id":860,"depth":916,"text":861},{"id":1163,"depth":916,"text":1164},{"id":1341,"depth":916,"text":1342},{"id":1777,"depth":916,"text":1778},{"id":1974,"depth":783,"text":1975},{"id":2015,"depth":783,"text":2016},"2023-11-06T00:00:00.000Z","How combining relational and non-relational databases in Django solved performance bottlenecks for a high-traffic e-commerce catalog microservice.",{"src":2041},"/images/blog/musictechlab_blog_hybrid-database-model-in-django-as-a-performance-booster.webp",{"enabled":794,"items":2043},[2044,2046,2049],{"text":2045,"icon":804},"Hybrid model achieved 8-10x speed improvement over standard Django for read-heavy queries.",{"text":2047,"icon":2048},"Average response time dropped from 120ms to 14ms by offloading product fields to MongoDB.","i-lucide-timer",{"text":2050,"icon":2051},"Descriptors and a metaclass keep the approach fully compatible with Django Admin and DRF.","i-lucide-code",{},{"title":156,"description":2039},[808,787,2055,809],"django","FusBV0SAjMJK8iTHoIGSjldwjXeuRCOnTQg-203Hs_A",{"id":2058,"title":112,"authors":2059,"badge":2065,"body":2068,"category":787,"client":741,"date":4347,"description":4348,"extension":790,"faq":741,"featured":69,"featuredOrder":741,"hidden":69,"image":4349,"keyTakeaways":4351,"meta":4363,"navigation":794,"path":113,"seo":4364,"status":741,"stem":114,"tags":4365,"teaser":741,"__hash__":4368,"score":935},"posts/blog/music-data/building-a-custom-music-delivery-platform-on-the-revelator-api.md",[2060],{"name":2061,"to":2062,"avatar":2063},"Mariusz Smenżyk","https://www.linkedin.com/in/mariusz-smenzyk/",{"src":2064},"/images/people/mariusz-smenzyk2.webp",{"label":2066,"color":2067},"Distribution","#0ea5e9",{"type":743,"value":2069,"toc":4315},[2070,2077,2080,2084,2087,2147,2150,2152,2156,2159,2168,2177,2184,2186,2190,2193,2196,2200,2219,2222,2251,2255,2266,2270,2281,2285,2296,2305,2307,2311,2314,2434,2438,2502,2506,2558,2562,2565,3303,3316,3318,3322,3325,3328,3332,3335,3564,3568,3571,3749,3753,3760,3956,3980,3984,3990,4008,4015,4017,4021,4024,4032,4036,4039,4043,4046,4102,4105,4109,4112,4116,4119,4121,4125,4128,4148,4151,4205,4214,4216,4220,4223,4249,4258,4260,4262,4265,4268,4270,4274,4312],[746,2071,2072,2073,2076],{},"Music distribution has never been more accessible. Platforms like DistroKid, TuneCore, and CD Baby democratized access to DSPs (Digital Service Providers) for independent artists. But for companies operating as distributors - managing ",[771,2074,2075],{"href":77},"multiple labels, complex territory restrictions",", and custom royalty splits - the generic UI and rigid workflows of consumer-facing tools quickly become a bottleneck.",[2078,2079],"hr",{},[750,2081,2083],{"id":2082},"why-distributors-outgrow-off-the-shelf-platforms","Why Distributors Outgrow Off-the-Shelf Platforms",[746,2085,2086],{},"The friction points are predictable:",[2088,2089,2090,2103],"table",{},[2091,2092,2093],"thead",{},[2094,2095,2096,2100],"tr",{},[2097,2098,2099],"th",{},"Pain Point",[2097,2101,2102],{},"What Happens",[2104,2105,2106,2117,2127,2137],"tbody",{},[2094,2107,2108,2114],{},[2109,2110,2111],"td",{},[850,2112,2113],{},"Territory restrictions",[2109,2115,2116],{},"Many platforms only support album-level territory settings, forcing workarounds when individual tracks have different rights across markets",[2094,2118,2119,2124],{},[2109,2120,2121],{},[850,2122,2123],{},"Metadata flexibility",[2109,2125,2126],{},"Contributor roles, localized titles, and DSP-specific artist IDs often require manual overrides the UI doesn't expose",[2094,2128,2129,2134],{},[2109,2130,2131],{},[850,2132,2133],{},"Reporting",[2109,2135,2136],{},"Off-the-shelf dashboards rarely match the operational needs of a multi-label distributor",[2094,2138,2139,2144],{},[2109,2140,2141],{},[850,2142,2143],{},"Delivery control",[2109,2145,2146],{},"When a platform handles delivery as a black box, debugging ingestion failures becomes a support ticket game",[746,2148,2149],{},"This is where the build-vs-buy decision becomes real.",[2078,2151],{},[750,2153,2155],{"id":2154},"the-two-paths-frontend-only-vs-full-stack","The Two Paths: Frontend-Only vs. Full Stack",[746,2157,2158],{},"Distributors who've outgrown their current setup face a fundamental architectural choice:",[2160,2161,2162],"note",{},[746,2163,2164,2167],{},[850,2165,2166],{},"Option A: Custom Frontend + Revelator API as Backend","\nBuild your own user-facing layer - UX, workflows, internal tools, reporting - and use Revelator's API purely for catalog management, delivery to DSPs, and royalty ingestion.",[2169,2170,2171],"warning",{},[746,2172,2173,2176],{},[850,2174,2175],{},"Option B: Full-Stack Distribution Platform","\nReplace the platform entirely. Build metadata management, DDEX (Digital Data Exchange) generation, delivery pipelines, DSP integrations, royalty ingestion, reporting, and payouts from scratch.",[746,2178,2179,2180,2183],{},"This article focuses on ",[850,2181,2182],{},"Option A",": what it looks like in practice, where it excels, and where it hits structural limits.",[2078,2185],{},[750,2187,2189],{"id":2188},"what-the-revelator-api-actually-offers","What the Revelator API Actually Offers",[746,2191,2192],{},"Revelator positions itself as an \"end-to-end operating system\" for independent music businesses. Unlike consumer-facing distributors, it's B2B infrastructure - designed for labels, aggregators, and distributors who want to run their own branded operation.",[746,2194,2195],{},"The API is RESTful, JSON-based, and covers five core modules:",[858,2197,2199],{"id":2198},"catalog-management","Catalog Management",[2201,2202,2203,2210,2213,2216],"ul",{},[2204,2205,2206,2207],"li",{},"Create and edit releases via ",[869,2208,2209],{},"POST /content/release/save",[2204,2211,2212],{},"Upload audio (WAV/FLAC, minimum 16-bit, 44.1 kHz stereo) and cover art (minimum 1400x1400px JPG/RGB)",[2204,2214,2215],{},"Manage ISRCs (International Standard Recording Codes), UPCs (Universal Product Codes), and external DSP artist IDs",[2204,2217,2218],{},"Support for multi-disc releases and localized metadata",[858,2220,2066],{"id":2221},"distribution",[2201,2223,2224,2230,2233,2239,2242,2248],{},[2204,2225,2226,2227],{},"Pre-delivery validation: ",[869,2228,2229],{},"POST /distribution/release/{releaseId}/validate",[2204,2231,2232],{},"Set DSP targets, release dates, and territories per release",[2204,2234,2235,2236],{},"Queue releases for delivery: ",[869,2237,2238],{},"POST /distribution/release/addtoqueue",[2204,2240,2241],{},"Track delivery status (statuses range from -20 to 100; 50+ means delivered)",[2204,2243,2244,2245],{},"Takedown support: ",[869,2246,2247],{},"POST /distribution/release/takedown",[2204,2249,2250],{},"Webhook callbacks for delivery status updates",[858,2252,2254],{"id":2253},"rights-and-royalties","Rights and Royalties",[2201,2256,2257,2260,2263],{},[2204,2258,2259],{},"Contract definition with configurable splits and recoupables",[2204,2261,2262],{},"DSP statement import and reconciliation",[2204,2264,2265],{},"Multi-currency payout automation via Tipalti and PayPal",[858,2267,2269],{"id":2268},"analytics","Analytics",[2201,2271,2272,2275,2278],{},[2204,2273,2274],{},"Streaming and revenue data queryable by track, release, region, or DSP",[2204,2276,2277],{},"Playlist performance tracking",[2204,2279,2280],{},"CSV export and raw data sync",[858,2282,2284],{"id":2283},"account-management","Account Management",[2201,2286,2287,2290,2293],{},[2204,2288,2289],{},"Parent-child account hierarchy (your enterprise is the parent; each label or artist is a child)",[2204,2291,2292],{},"Full visibility into child account assets",[2204,2294,2295],{},"Mandatory approval step before content reaches DSPs",[2004,2297,2298],{},[746,2299,2300,2301,2304],{},"Revelator claims ",[850,2302,2303],{},"100+ DSP integrations",", including Spotify, Apple Music, Amazon, YouTube Music, YouTube Content ID, TikTok, and Deezer. DSP access is configurable per child account.",[2078,2306],{},[750,2308,2310],{"id":2309},"architecture-of-the-hybrid-solution","Architecture of the Hybrid Solution",[746,2312,2313],{},"The hybrid approach layers your custom platform on top of Revelator's delivery infrastructure:",[863,2315,2319],{"className":2316,"code":2317,"language":2318,"meta":782,"style":782},"language-mermaid shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","graph TD\n    subgraph Frontend[Your Custom Frontend]\n        AP[Artist Portal]\n        LD[Label Dashboard]\n        AT[Admin Tools]\n        AP & LD & AT --> BE[Your API / Backend]\n    end\n    subgraph Revelator[Revelator API Layer]\n        CAT[Catalog Mgmt API]\n        DEL[Delivery API]\n        ROY[Royalty Ingestion]\n    end\n    BE --> CAT & DEL & ROY\n    subgraph DSPs[Digital Service Providers]\n        SP[Spotify]\n        AM[Apple Music]\n        YT[YouTube]\n        MORE[100+ more]\n    end\n    DEL --> SP & AM & YT & MORE\n    style Frontend fill:#0f172a,stroke:#38bdf8,color:#f8fafc\n    style Revelator fill:#1e293b,stroke:#0ea5e9,color:#f8fafc\n    style DSPs fill:#0f172a,stroke:#334155,color:#f8fafc\n","mermaid",[869,2320,2321,2326,2331,2336,2341,2346,2351,2356,2361,2366,2371,2376,2380,2385,2390,2395,2400,2405,2410,2414,2419,2424,2429],{"__ignoreMap":782},[872,2322,2323],{"class":874,"line":875},[872,2324,2325],{"class":919},"graph TD\n",[872,2327,2328],{"class":874,"line":783},[872,2329,2330],{"class":919},"    subgraph Frontend[Your Custom Frontend]\n",[872,2332,2333],{"class":874,"line":916},[872,2334,2335],{"class":919},"        AP[Artist Portal]\n",[872,2337,2338],{"class":874,"line":935},[872,2339,2340],{"class":919},"        LD[Label Dashboard]\n",[872,2342,2343],{"class":874,"line":966},[872,2344,2345],{"class":919},"        AT[Admin Tools]\n",[872,2347,2348],{"class":874,"line":972},[872,2349,2350],{"class":919},"        AP & LD & AT --> BE[Your API / Backend]\n",[872,2352,2353],{"class":874,"line":997},[872,2354,2355],{"class":919},"    end\n",[872,2357,2358],{"class":874,"line":1013},[872,2359,2360],{"class":919},"    subgraph Revelator[Revelator API Layer]\n",[872,2362,2363],{"class":874,"line":1022},[872,2364,2365],{"class":919},"        CAT[Catalog Mgmt API]\n",[872,2367,2368],{"class":874,"line":1029},[872,2369,2370],{"class":919},"        DEL[Delivery API]\n",[872,2372,2373],{"class":874,"line":1043},[872,2374,2375],{"class":919},"        ROY[Royalty Ingestion]\n",[872,2377,2378],{"class":874,"line":1070},[872,2379,2355],{"class":919},[872,2381,2382],{"class":874,"line":1098},[872,2383,2384],{"class":919},"    BE --> CAT & DEL & ROY\n",[872,2386,2387],{"class":874,"line":1103},[872,2388,2389],{"class":919},"    subgraph DSPs[Digital Service Providers]\n",[872,2391,2392],{"class":874,"line":1126},[872,2393,2394],{"class":919},"        SP[Spotify]\n",[872,2396,2397],{"class":874,"line":1132},[872,2398,2399],{"class":919},"        AM[Apple Music]\n",[872,2401,2402],{"class":874,"line":1586},[872,2403,2404],{"class":919},"        YT[YouTube]\n",[872,2406,2407],{"class":874,"line":1597},[872,2408,2409],{"class":919},"        MORE[100+ more]\n",[872,2411,2412],{"class":874,"line":1614},[872,2413,2355],{"class":919},[872,2415,2416],{"class":874,"line":1634},[872,2417,2418],{"class":919},"    DEL --> SP & AM & YT & MORE\n",[872,2420,2421],{"class":874,"line":1654},[872,2422,2423],{"class":919},"    style Frontend fill:#0f172a,stroke:#38bdf8,color:#f8fafc\n",[872,2425,2426],{"class":874,"line":1682},[872,2427,2428],{"class":919},"    style Revelator fill:#1e293b,stroke:#0ea5e9,color:#f8fafc\n",[872,2430,2431],{"class":874,"line":1692},[872,2432,2433],{"class":919},"    style DSPs fill:#0f172a,stroke:#334155,color:#f8fafc\n",[858,2435,2437],{"id":2436},"what-you-build","What You Build",[2088,2439,2440,2450],{},[2091,2441,2442],{},[2094,2443,2444,2447],{},[2097,2445,2446],{},"Layer",[2097,2448,2449],{},"Responsibility",[2104,2451,2452,2462,2472,2482,2492],{},[2094,2453,2454,2459],{},[2109,2455,2456],{},[850,2457,2458],{},"Artist & label portals",[2109,2460,2461],{},"Branded onboarding, release submission, approval workflows",[2094,2463,2464,2469],{},[2109,2465,2466],{},[850,2467,2468],{},"Metadata layer",[2109,2470,2471],{},"Your own database of releases, tracks, contributors, and rights - synced to Revelator via API",[2094,2473,2474,2479],{},[2109,2475,2476],{},[850,2477,2478],{},"Territory & rights engine",[2109,2480,2481],{},"Business logic for track-level territory restrictions, split sheets, and embargo rules",[2094,2483,2484,2489],{},[2109,2485,2486],{},[850,2487,2488],{},"Reporting dashboards",[2109,2490,2491],{},"Custom views pulling from Revelator's analytics API plus your own data",[2094,2493,2494,2499],{},[2109,2495,2496],{},[850,2497,2498],{},"Internal tools",[2109,2500,2501],{},"Approval queues, QC checklists, bulk operations, CRM integration",[858,2503,2505],{"id":2504},"what-revelator-handles","What Revelator Handles",[2088,2507,2508,2516],{},[2091,2509,2510],{},[2094,2511,2512,2514],{},[2097,2513,2446],{},[2097,2515,2449],{},[2104,2517,2518,2528,2538,2548],{},[2094,2519,2520,2525],{},[2109,2521,2522],{},[850,2523,2524],{},"DDEX generation",[2109,2526,2527],{},"Generates ERN (Electronic Release Notification) XML packages from catalog data you push via API",[2094,2529,2530,2535],{},[2109,2531,2532],{},[850,2533,2534],{},"DSP delivery",[2109,2536,2537],{},"SFTP/API delivery to all connected DSPs, including retry logic and status tracking",[2094,2539,2540,2545],{},[2109,2541,2542],{},[850,2543,2544],{},"Royalty ingestion",[2109,2546,2547],{},"Parsing DSP statements and normalizing revenue data",[2094,2549,2550,2555],{},[2109,2551,2552],{},[850,2553,2554],{},"Payout infrastructure",[2109,2556,2557],{},"Payment rail integrations for artist payouts",[858,2559,2561],{"id":2560},"quick-example-creating-a-release-via-revelator-api","Quick Example: Creating a Release via Revelator API",[746,2563,2564],{},"Here's a minimal example of how your backend would create and distribute a release through Revelator's API:",[863,2566,2568],{"className":865,"code":2567,"language":867,"meta":782,"style":782},"import httpx\n\nREVELATOR_API = \"https://api.revelator.com\"\n\n# 1. Authenticate\nauth = httpx.post(f\"{REVELATOR_API}/partner/account/login\", json={\n    \"email\": \"your@email.com\",\n    \"password\": \"your-password\"\n})\ntoken = auth.json()[\"token\"]\nheaders = {\"Authorization\": f\"Bearer {token}\"}\n\n# 2. Create a release\nrelease = httpx.post(f\"{REVELATOR_API}/content/release/save\", headers=headers, json={\n    \"title\": \"Delilah - Summer Version\",\n    \"releaseType\": \"Single\",\n    \"artists\": [\n        {\"name\": \"MIKOLAS\", \"role\": \"MainArtist\"},\n        {\"name\": \"Mark Neve\", \"role\": \"MainArtist\"}\n    ],\n    \"genre\": \"Pop\",\n    \"releaseDate\": \"2026-04-01\",\n    \"territories\": {\n        \"include\": \"Worldwide\",\n        \"exclude\": [\"JP\", \"DE\", \"AT\", \"CH\", \"NL\", \"AU\", \"NZ\"]\n    }\n})\nrelease_id = release.json()[\"id\"]\n\n# 3. Validate before delivery\nvalidation = httpx.post(\n    f\"{REVELATOR_API}/distribution/release/{release_id}/validate\",\n    headers=headers\n)\n\n# 4. Queue for delivery to DSPs\nif validation.json()[\"valid\"]:\n    httpx.post(f\"{REVELATOR_API}/distribution/release/addtoqueue\",\n        headers=headers,\n        json={\"releaseId\": release_id}\n    )\n",[869,2569,2570,2578,2582,2596,2600,2605,2645,2667,2685,2690,2717,2752,2756,2761,2805,2825,2845,2859,2899,2936,2941,2961,2981,2995,3016,3092,3097,3101,3127,3132,3138,3154,3183,3194,3199,3204,3210,3235,3262,3274,3297],{"__ignoreMap":782},[872,2571,2572,2575],{"class":874,"line":875},[872,2573,2574],{"class":1000},"import",[872,2576,2577],{"class":919}," httpx\n",[872,2579,2580],{"class":874,"line":783},[872,2581,969],{"emptyLinePlaceholder":794},[872,2583,2584,2587,2589,2591,2594],{"class":874,"line":916},[872,2585,2586],{"class":919},"REVELATOR_API ",[872,2588,1231],{"class":886},[872,2590,1277],{"class":886},[872,2592,2593],{"class":950},"https://api.revelator.com",[872,2595,963],{"class":886},[872,2597,2598],{"class":874,"line":935},[872,2599,969],{"emptyLinePlaceholder":794},[872,2601,2602],{"class":874,"line":966},[872,2603,2604],{"class":1025},"# 1. Authenticate\n",[872,2606,2607,2610,2612,2615,2617,2620,2622,2625,2627,2629,2632,2634,2637,2639,2642],{"class":874,"line":972},[872,2608,2609],{"class":919},"auth ",[872,2611,1231],{"class":886},[872,2613,2614],{"class":919}," httpx",[872,2616,777],{"class":886},[872,2618,2619],{"class":895},"post",[872,2621,899],{"class":886},[872,2623,2624],{"class":878},"f",[872,2626,1283],{"class":950},[872,2628,955],{"class":954},[872,2630,2631],{"class":895},"REVELATOR_API",[872,2633,960],{"class":954},[872,2635,2636],{"class":950},"/partner/account/login\"",[872,2638,906],{"class":886},[872,2640,2641],{"class":909}," json",[872,2643,2644],{"class":886},"={\n",[872,2646,2647,2650,2653,2655,2657,2659,2662,2664],{"class":874,"line":997},[872,2648,2649],{"class":886},"    \"",[872,2651,2652],{"class":950},"email",[872,2654,1283],{"class":886},[872,2656,1508],{"class":886},[872,2658,1277],{"class":886},[872,2660,2661],{"class":950},"your@email.com",[872,2663,1283],{"class":886},[872,2665,2666],{"class":886},",\n",[872,2668,2669,2671,2674,2676,2678,2680,2683],{"class":874,"line":1013},[872,2670,2649],{"class":886},[872,2672,2673],{"class":950},"password",[872,2675,1283],{"class":886},[872,2677,1508],{"class":886},[872,2679,1277],{"class":886},[872,2681,2682],{"class":950},"your-password",[872,2684,963],{"class":886},[872,2686,2687],{"class":874,"line":1022},[872,2688,2689],{"class":886},"})\n",[872,2691,2692,2695,2697,2700,2702,2705,2708,2710,2713,2715],{"class":874,"line":1029},[872,2693,2694],{"class":919},"token ",[872,2696,1231],{"class":886},[872,2698,2699],{"class":919}," auth",[872,2701,777],{"class":886},[872,2703,2704],{"class":895},"json",[872,2706,2707],{"class":886},"()[",[872,2709,1283],{"class":886},[872,2711,2712],{"class":950},"token",[872,2714,1283],{"class":886},[872,2716,1925],{"class":886},[872,2718,2719,2722,2724,2727,2729,2732,2734,2736,2738,2741,2743,2745,2747,2749],{"class":874,"line":1043},[872,2720,2721],{"class":919},"headers ",[872,2723,1231],{"class":886},[872,2725,2726],{"class":886}," {",[872,2728,1283],{"class":886},[872,2730,2731],{"class":950},"Authorization",[872,2733,1283],{"class":886},[872,2735,1508],{"class":886},[872,2737,947],{"class":878},[872,2739,2740],{"class":950},"\"Bearer ",[872,2742,955],{"class":954},[872,2744,2712],{"class":919},[872,2746,960],{"class":954},[872,2748,1283],{"class":950},[872,2750,2751],{"class":886},"}\n",[872,2753,2754],{"class":874,"line":1070},[872,2755,969],{"emptyLinePlaceholder":794},[872,2757,2758],{"class":874,"line":1098},[872,2759,2760],{"class":1025},"# 2. Create a release\n",[872,2762,2763,2766,2768,2770,2772,2774,2776,2778,2780,2782,2784,2786,2789,2791,2794,2796,2799,2801,2803],{"class":874,"line":1103},[872,2764,2765],{"class":919},"release ",[872,2767,1231],{"class":886},[872,2769,2614],{"class":919},[872,2771,777],{"class":886},[872,2773,2619],{"class":895},[872,2775,899],{"class":886},[872,2777,2624],{"class":878},[872,2779,1283],{"class":950},[872,2781,955],{"class":954},[872,2783,2631],{"class":895},[872,2785,960],{"class":954},[872,2787,2788],{"class":950},"/content/release/save\"",[872,2790,906],{"class":886},[872,2792,2793],{"class":909}," headers",[872,2795,1231],{"class":886},[872,2797,2798],{"class":895},"headers",[872,2800,906],{"class":886},[872,2802,2641],{"class":909},[872,2804,2644],{"class":886},[872,2806,2807,2809,2812,2814,2816,2818,2821,2823],{"class":874,"line":1126},[872,2808,2649],{"class":886},[872,2810,2811],{"class":950},"title",[872,2813,1283],{"class":886},[872,2815,1508],{"class":886},[872,2817,1277],{"class":886},[872,2819,2820],{"class":950},"Delilah - Summer Version",[872,2822,1283],{"class":886},[872,2824,2666],{"class":886},[872,2826,2827,2829,2832,2834,2836,2838,2841,2843],{"class":874,"line":1132},[872,2828,2649],{"class":886},[872,2830,2831],{"class":950},"releaseType",[872,2833,1283],{"class":886},[872,2835,1508],{"class":886},[872,2837,1277],{"class":886},[872,2839,2840],{"class":950},"Single",[872,2842,1283],{"class":886},[872,2844,2666],{"class":886},[872,2846,2847,2849,2852,2854,2856],{"class":874,"line":1586},[872,2848,2649],{"class":886},[872,2850,2851],{"class":950},"artists",[872,2853,1283],{"class":886},[872,2855,1508],{"class":886},[872,2857,2858],{"class":886}," [\n",[872,2860,2861,2864,2866,2868,2870,2872,2874,2877,2879,2881,2883,2886,2888,2890,2892,2895,2897],{"class":874,"line":1597},[872,2862,2863],{"class":886},"        {",[872,2865,1283],{"class":886},[872,2867,1884],{"class":950},[872,2869,1283],{"class":886},[872,2871,1508],{"class":886},[872,2873,1277],{"class":886},[872,2875,2876],{"class":950},"MIKOLAS",[872,2878,1283],{"class":886},[872,2880,906],{"class":886},[872,2882,1277],{"class":886},[872,2884,2885],{"class":950},"role",[872,2887,1283],{"class":886},[872,2889,1508],{"class":886},[872,2891,1277],{"class":886},[872,2893,2894],{"class":950},"MainArtist",[872,2896,1283],{"class":886},[872,2898,1752],{"class":886},[872,2900,2901,2903,2905,2907,2909,2911,2913,2916,2918,2920,2922,2924,2926,2928,2930,2932,2934],{"class":874,"line":1614},[872,2902,2863],{"class":886},[872,2904,1283],{"class":886},[872,2906,1884],{"class":950},[872,2908,1283],{"class":886},[872,2910,1508],{"class":886},[872,2912,1277],{"class":886},[872,2914,2915],{"class":950},"Mark Neve",[872,2917,1283],{"class":886},[872,2919,906],{"class":886},[872,2921,1277],{"class":886},[872,2923,2885],{"class":950},[872,2925,1283],{"class":886},[872,2927,1508],{"class":886},[872,2929,1277],{"class":886},[872,2931,2894],{"class":950},[872,2933,1283],{"class":886},[872,2935,2751],{"class":886},[872,2937,2938],{"class":874,"line":1634},[872,2939,2940],{"class":886},"    ],\n",[872,2942,2943,2945,2948,2950,2952,2954,2957,2959],{"class":874,"line":1654},[872,2944,2649],{"class":886},[872,2946,2947],{"class":950},"genre",[872,2949,1283],{"class":886},[872,2951,1508],{"class":886},[872,2953,1277],{"class":886},[872,2955,2956],{"class":950},"Pop",[872,2958,1283],{"class":886},[872,2960,2666],{"class":886},[872,2962,2963,2965,2968,2970,2972,2974,2977,2979],{"class":874,"line":1682},[872,2964,2649],{"class":886},[872,2966,2967],{"class":950},"releaseDate",[872,2969,1283],{"class":886},[872,2971,1508],{"class":886},[872,2973,1277],{"class":886},[872,2975,2976],{"class":950},"2026-04-01",[872,2978,1283],{"class":886},[872,2980,2666],{"class":886},[872,2982,2983,2985,2988,2990,2992],{"class":874,"line":1692},[872,2984,2649],{"class":886},[872,2986,2987],{"class":950},"territories",[872,2989,1283],{"class":886},[872,2991,1508],{"class":886},[872,2993,2994],{"class":886}," {\n",[872,2996,2997,3000,3003,3005,3007,3009,3012,3014],{"class":874,"line":1709},[872,2998,2999],{"class":886},"        \"",[872,3001,3002],{"class":950},"include",[872,3004,1283],{"class":886},[872,3006,1508],{"class":886},[872,3008,1277],{"class":886},[872,3010,3011],{"class":950},"Worldwide",[872,3013,1283],{"class":886},[872,3015,2666],{"class":886},[872,3017,3018,3020,3023,3025,3027,3029,3031,3034,3036,3038,3040,3043,3045,3047,3049,3052,3054,3056,3058,3061,3063,3065,3067,3070,3072,3074,3076,3079,3081,3083,3085,3088,3090],{"class":874,"line":1736},[872,3019,2999],{"class":886},[872,3021,3022],{"class":950},"exclude",[872,3024,1283],{"class":886},[872,3026,1508],{"class":886},[872,3028,1879],{"class":886},[872,3030,1283],{"class":886},[872,3032,3033],{"class":950},"JP",[872,3035,1283],{"class":886},[872,3037,906],{"class":886},[872,3039,1277],{"class":886},[872,3041,3042],{"class":950},"DE",[872,3044,1283],{"class":886},[872,3046,906],{"class":886},[872,3048,1277],{"class":886},[872,3050,3051],{"class":950},"AT",[872,3053,1283],{"class":886},[872,3055,906],{"class":886},[872,3057,1277],{"class":886},[872,3059,3060],{"class":950},"CH",[872,3062,1283],{"class":886},[872,3064,906],{"class":886},[872,3066,1277],{"class":886},[872,3068,3069],{"class":950},"NL",[872,3071,1283],{"class":886},[872,3073,906],{"class":886},[872,3075,1277],{"class":886},[872,3077,3078],{"class":950},"AU",[872,3080,1283],{"class":886},[872,3082,906],{"class":886},[872,3084,1277],{"class":886},[872,3086,3087],{"class":950},"NZ",[872,3089,1283],{"class":886},[872,3091,1925],{"class":886},[872,3093,3094],{"class":874,"line":1755},[872,3095,3096],{"class":886},"    }\n",[872,3098,3099],{"class":874,"line":1764},[872,3100,2689],{"class":886},[872,3102,3104,3107,3109,3112,3114,3116,3118,3120,3123,3125],{"class":874,"line":3103},28,[872,3105,3106],{"class":919},"release_id ",[872,3108,1231],{"class":886},[872,3110,3111],{"class":919}," release",[872,3113,777],{"class":886},[872,3115,2704],{"class":895},[872,3117,2707],{"class":886},[872,3119,1283],{"class":886},[872,3121,3122],{"class":950},"id",[872,3124,1283],{"class":886},[872,3126,1925],{"class":886},[872,3128,3130],{"class":874,"line":3129},29,[872,3131,969],{"emptyLinePlaceholder":794},[872,3133,3135],{"class":874,"line":3134},30,[872,3136,3137],{"class":1025},"# 3. Validate before delivery\n",[872,3139,3141,3144,3146,3148,3150,3152],{"class":874,"line":3140},31,[872,3142,3143],{"class":919},"validation ",[872,3145,1231],{"class":886},[872,3147,2614],{"class":919},[872,3149,777],{"class":886},[872,3151,2619],{"class":895},[872,3153,1493],{"class":886},[872,3155,3157,3160,3162,3164,3166,3168,3171,3173,3176,3178,3181],{"class":874,"line":3156},32,[872,3158,3159],{"class":878},"    f",[872,3161,1283],{"class":950},[872,3163,955],{"class":954},[872,3165,2631],{"class":895},[872,3167,960],{"class":954},[872,3169,3170],{"class":950},"/distribution/release/",[872,3172,955],{"class":954},[872,3174,3175],{"class":895},"release_id",[872,3177,960],{"class":954},[872,3179,3180],{"class":950},"/validate\"",[872,3182,2666],{"class":886},[872,3184,3186,3189,3191],{"class":874,"line":3185},33,[872,3187,3188],{"class":909},"    headers",[872,3190,1231],{"class":886},[872,3192,3193],{"class":895},"headers\n",[872,3195,3197],{"class":874,"line":3196},34,[872,3198,1067],{"class":886},[872,3200,3202],{"class":874,"line":3201},35,[872,3203,969],{"emptyLinePlaceholder":794},[872,3205,3207],{"class":874,"line":3206},36,[872,3208,3209],{"class":1025},"# 4. Queue for delivery to DSPs\n",[872,3211,3213,3216,3219,3221,3223,3225,3227,3230,3232],{"class":874,"line":3212},37,[872,3214,3215],{"class":1000},"if",[872,3217,3218],{"class":919}," validation",[872,3220,777],{"class":886},[872,3222,2704],{"class":895},[872,3224,2707],{"class":886},[872,3226,1283],{"class":886},[872,3228,3229],{"class":950},"valid",[872,3231,1283],{"class":886},[872,3233,3234],{"class":886},"]:\n",[872,3236,3238,3241,3243,3245,3247,3249,3251,3253,3255,3257,3260],{"class":874,"line":3237},38,[872,3239,3240],{"class":919},"    httpx",[872,3242,777],{"class":886},[872,3244,2619],{"class":895},[872,3246,899],{"class":886},[872,3248,2624],{"class":878},[872,3250,1283],{"class":950},[872,3252,955],{"class":954},[872,3254,2631],{"class":895},[872,3256,960],{"class":954},[872,3258,3259],{"class":950},"/distribution/release/addtoqueue\"",[872,3261,2666],{"class":886},[872,3263,3265,3268,3270,3272],{"class":874,"line":3264},39,[872,3266,3267],{"class":909},"        headers",[872,3269,1231],{"class":886},[872,3271,2798],{"class":895},[872,3273,2666],{"class":886},[872,3275,3277,3280,3283,3285,3288,3290,3292,3295],{"class":874,"line":3276},40,[872,3278,3279],{"class":909},"        json",[872,3281,3282],{"class":886},"={",[872,3284,1283],{"class":886},[872,3286,3287],{"class":950},"releaseId",[872,3289,1283],{"class":886},[872,3291,1508],{"class":886},[872,3293,3294],{"class":895}," release_id",[872,3296,2751],{"class":886},[872,3298,3300],{"class":874,"line":3299},41,[872,3301,3302],{"class":886},"    )\n",[2160,3304,3305],{},[746,3306,3307,3308,3311,3312,3315],{},"This is a simplified example. In production, you'd also upload audio files via ",[869,3309,3310],{},"/media/audio/upload",", cover art via ",[869,3313,3314],{},"/media/image/upload",", and handle webhook callbacks for delivery status updates.",[2078,3317],{},[750,3319,3321],{"id":3320},"ddex-as-the-backbone","DDEX as the Backbone",[746,3323,3324],{},"Under the hood, every delivery to a DSP is a DDEX ERN message - an XML package describing the release, its resources (audio, artwork), and the commercial terms (deals) under which DSPs can exploit the content.",[746,3326,3327],{},"An ERN message has three critical sections:",[858,3329,3331],{"id":3330},"resourcelist","ResourceList",[746,3333,3334],{},"Defines the audio files, cover art, and their metadata. Each sound recording includes an ISRC, title, artist credits, and territory-specific details:",[863,3336,3340],{"className":3337,"code":3338,"language":3339,"meta":782,"style":782},"language-xml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003CSoundRecording>\n  \u003CSoundRecordingId>\n    \u003CISRC>SKXXX2500001\u003C/ISRC>\n  \u003C/SoundRecordingId>\n  \u003CResourceReference>A1\u003C/ResourceReference>\n  \u003CSoundRecordingDetailsByTerritory>\n    \u003CTerritoryCode>Worldwide\u003C/TerritoryCode>\n    \u003CTitle TitleType=\"FormalTitle\">\n      \u003CTitleText>Delilah\u003C/TitleText>\n    \u003C/Title>\n    \u003CDisplayArtist>\n      \u003CPartyName>\u003CFullName>MIKOLAS\u003C/FullName>\u003C/PartyName>\n      \u003CArtistRole>MainArtist\u003C/ArtistRole>\n    \u003C/DisplayArtist>\n  \u003C/SoundRecordingDetailsByTerritory>\n\u003C/SoundRecording>\n","xml",[869,3341,3342,3353,3363,3384,3393,3411,3420,3437,3458,3477,3486,3495,3523,3540,3548,3556],{"__ignoreMap":782},[872,3343,3344,3347,3350],{"class":874,"line":875},[872,3345,3346],{"class":886},"\u003C",[872,3348,3349],{"class":925},"SoundRecording",[872,3351,3352],{"class":886},">\n",[872,3354,3355,3358,3361],{"class":874,"line":783},[872,3356,3357],{"class":886},"  \u003C",[872,3359,3360],{"class":925},"SoundRecordingId",[872,3362,3352],{"class":886},[872,3364,3365,3368,3371,3374,3377,3380,3382],{"class":874,"line":916},[872,3366,3367],{"class":886},"    \u003C",[872,3369,3370],{"class":925},"ISRC",[872,3372,3373],{"class":886},">",[872,3375,3376],{"class":919},"SKXXX2500001",[872,3378,3379],{"class":886},"\u003C/",[872,3381,3370],{"class":925},[872,3383,3352],{"class":886},[872,3385,3386,3389,3391],{"class":874,"line":935},[872,3387,3388],{"class":886},"  \u003C/",[872,3390,3360],{"class":925},[872,3392,3352],{"class":886},[872,3394,3395,3397,3400,3402,3405,3407,3409],{"class":874,"line":966},[872,3396,3357],{"class":886},[872,3398,3399],{"class":925},"ResourceReference",[872,3401,3373],{"class":886},[872,3403,3404],{"class":919},"A1",[872,3406,3379],{"class":886},[872,3408,3399],{"class":925},[872,3410,3352],{"class":886},[872,3412,3413,3415,3418],{"class":874,"line":972},[872,3414,3357],{"class":886},[872,3416,3417],{"class":925},"SoundRecordingDetailsByTerritory",[872,3419,3352],{"class":886},[872,3421,3422,3424,3427,3429,3431,3433,3435],{"class":874,"line":997},[872,3423,3367],{"class":886},[872,3425,3426],{"class":925},"TerritoryCode",[872,3428,3373],{"class":886},[872,3430,3011],{"class":919},[872,3432,3379],{"class":886},[872,3434,3426],{"class":925},[872,3436,3352],{"class":886},[872,3438,3439,3441,3444,3447,3449,3451,3454,3456],{"class":874,"line":1013},[872,3440,3367],{"class":886},[872,3442,3443],{"class":925},"Title",[872,3445,3446],{"class":878}," TitleType",[872,3448,1231],{"class":886},[872,3450,1283],{"class":886},[872,3452,3453],{"class":950},"FormalTitle",[872,3455,1283],{"class":886},[872,3457,3352],{"class":886},[872,3459,3460,3463,3466,3468,3471,3473,3475],{"class":874,"line":1022},[872,3461,3462],{"class":886},"      \u003C",[872,3464,3465],{"class":925},"TitleText",[872,3467,3373],{"class":886},[872,3469,3470],{"class":919},"Delilah",[872,3472,3379],{"class":886},[872,3474,3465],{"class":925},[872,3476,3352],{"class":886},[872,3478,3479,3482,3484],{"class":874,"line":1029},[872,3480,3481],{"class":886},"    \u003C/",[872,3483,3443],{"class":925},[872,3485,3352],{"class":886},[872,3487,3488,3490,3493],{"class":874,"line":1043},[872,3489,3367],{"class":886},[872,3491,3492],{"class":925},"DisplayArtist",[872,3494,3352],{"class":886},[872,3496,3497,3499,3502,3505,3508,3510,3512,3514,3516,3519,3521],{"class":874,"line":1070},[872,3498,3462],{"class":886},[872,3500,3501],{"class":925},"PartyName",[872,3503,3504],{"class":886},">\u003C",[872,3506,3507],{"class":925},"FullName",[872,3509,3373],{"class":886},[872,3511,2876],{"class":919},[872,3513,3379],{"class":886},[872,3515,3507],{"class":925},[872,3517,3518],{"class":886},">\u003C/",[872,3520,3501],{"class":925},[872,3522,3352],{"class":886},[872,3524,3525,3527,3530,3532,3534,3536,3538],{"class":874,"line":1098},[872,3526,3462],{"class":886},[872,3528,3529],{"class":925},"ArtistRole",[872,3531,3373],{"class":886},[872,3533,2894],{"class":919},[872,3535,3379],{"class":886},[872,3537,3529],{"class":925},[872,3539,3352],{"class":886},[872,3541,3542,3544,3546],{"class":874,"line":1103},[872,3543,3481],{"class":886},[872,3545,3492],{"class":925},[872,3547,3352],{"class":886},[872,3549,3550,3552,3554],{"class":874,"line":1126},[872,3551,3388],{"class":886},[872,3553,3417],{"class":925},[872,3555,3352],{"class":886},[872,3557,3558,3560,3562],{"class":874,"line":1132},[872,3559,3379],{"class":886},[872,3561,3349],{"class":925},[872,3563,3352],{"class":886},[858,3565,3567],{"id":3566},"releaselist","ReleaseList",[746,3569,3570],{},"Defines the product - the album or single - and links it to its component resources:",[863,3572,3574],{"className":3337,"code":3573,"language":3339,"meta":782,"style":782},"\u003CRelease>\n  \u003CReleaseId>\u003CICPN>0123456789012\u003C/ICPN>\u003C/ReleaseId>\n  \u003CReleaseReference>R0\u003C/ReleaseReference>\n  \u003CReleaseType>Album\u003C/ReleaseType>\n  \u003CReleaseDetailsByTerritory>\n    \u003CTerritoryCode>Worldwide\u003C/TerritoryCode>\n    \u003CDisplayArtistName>MIKOLAS\u003C/DisplayArtistName>\n    \u003CTitle TitleType=\"FormalTitle\">\n      \u003CTitleText>ONE\u003C/TitleText>\n    \u003C/Title>\n  \u003C/ReleaseDetailsByTerritory>\n\u003C/Release>\n",[869,3575,3576,3585,3612,3630,3648,3657,3673,3690,3708,3725,3733,3741],{"__ignoreMap":782},[872,3577,3578,3580,3583],{"class":874,"line":875},[872,3579,3346],{"class":886},[872,3581,3582],{"class":925},"Release",[872,3584,3352],{"class":886},[872,3586,3587,3589,3592,3594,3597,3599,3602,3604,3606,3608,3610],{"class":874,"line":783},[872,3588,3357],{"class":886},[872,3590,3591],{"class":925},"ReleaseId",[872,3593,3504],{"class":886},[872,3595,3596],{"class":925},"ICPN",[872,3598,3373],{"class":886},[872,3600,3601],{"class":919},"0123456789012",[872,3603,3379],{"class":886},[872,3605,3596],{"class":925},[872,3607,3518],{"class":886},[872,3609,3591],{"class":925},[872,3611,3352],{"class":886},[872,3613,3614,3616,3619,3621,3624,3626,3628],{"class":874,"line":916},[872,3615,3357],{"class":886},[872,3617,3618],{"class":925},"ReleaseReference",[872,3620,3373],{"class":886},[872,3622,3623],{"class":919},"R0",[872,3625,3379],{"class":886},[872,3627,3618],{"class":925},[872,3629,3352],{"class":886},[872,3631,3632,3634,3637,3639,3642,3644,3646],{"class":874,"line":935},[872,3633,3357],{"class":886},[872,3635,3636],{"class":925},"ReleaseType",[872,3638,3373],{"class":886},[872,3640,3641],{"class":919},"Album",[872,3643,3379],{"class":886},[872,3645,3636],{"class":925},[872,3647,3352],{"class":886},[872,3649,3650,3652,3655],{"class":874,"line":966},[872,3651,3357],{"class":886},[872,3653,3654],{"class":925},"ReleaseDetailsByTerritory",[872,3656,3352],{"class":886},[872,3658,3659,3661,3663,3665,3667,3669,3671],{"class":874,"line":972},[872,3660,3367],{"class":886},[872,3662,3426],{"class":925},[872,3664,3373],{"class":886},[872,3666,3011],{"class":919},[872,3668,3379],{"class":886},[872,3670,3426],{"class":925},[872,3672,3352],{"class":886},[872,3674,3675,3677,3680,3682,3684,3686,3688],{"class":874,"line":997},[872,3676,3367],{"class":886},[872,3678,3679],{"class":925},"DisplayArtistName",[872,3681,3373],{"class":886},[872,3683,2876],{"class":919},[872,3685,3379],{"class":886},[872,3687,3679],{"class":925},[872,3689,3352],{"class":886},[872,3691,3692,3694,3696,3698,3700,3702,3704,3706],{"class":874,"line":1013},[872,3693,3367],{"class":886},[872,3695,3443],{"class":925},[872,3697,3446],{"class":878},[872,3699,1231],{"class":886},[872,3701,1283],{"class":886},[872,3703,3453],{"class":950},[872,3705,1283],{"class":886},[872,3707,3352],{"class":886},[872,3709,3710,3712,3714,3716,3719,3721,3723],{"class":874,"line":1022},[872,3711,3462],{"class":886},[872,3713,3465],{"class":925},[872,3715,3373],{"class":886},[872,3717,3718],{"class":919},"ONE",[872,3720,3379],{"class":886},[872,3722,3465],{"class":925},[872,3724,3352],{"class":886},[872,3726,3727,3729,3731],{"class":874,"line":1029},[872,3728,3481],{"class":886},[872,3730,3443],{"class":925},[872,3732,3352],{"class":886},[872,3734,3735,3737,3739],{"class":874,"line":1043},[872,3736,3388],{"class":886},[872,3738,3654],{"class":925},[872,3740,3352],{"class":886},[872,3742,3743,3745,3747],{"class":874,"line":1070},[872,3744,3379],{"class":886},[872,3746,3582],{"class":925},[872,3748,3352],{"class":886},[858,3750,3752],{"id":3751},"deallist","DealList",[746,3754,3755,3756,3759],{},"The ",[850,3757,3758],{},"only"," section that grants commercial rights. This is where territory restrictions live:",[863,3761,3763],{"className":3337,"code":3762,"language":3339,"meta":782,"style":782},"\u003CReleaseDeal>\n  \u003CDealReleaseReference>R0\u003C/DealReleaseReference>\n  \u003CDeal>\n    \u003CDealTerms>\n      \u003CTerritoryCode>Worldwide\u003C/TerritoryCode>\n      \u003CExcludedTerritoryCode>JP\u003C/ExcludedTerritoryCode>\n      \u003CCommercialModelType>SubscriptionModel\u003C/CommercialModelType>\n      \u003CUsage>\n        \u003CUseType>OnDemandStream\u003C/UseType>\n      \u003C/Usage>\n      \u003CValidityPeriod>\n        \u003CStartDate>2026-03-15\u003C/StartDate>\n      \u003C/ValidityPeriod>\n    \u003C/DealTerms>\n  \u003C/Deal>\n\u003C/ReleaseDeal>\n",[869,3764,3765,3774,3791,3800,3809,3825,3842,3860,3869,3888,3897,3906,3924,3932,3940,3948],{"__ignoreMap":782},[872,3766,3767,3769,3772],{"class":874,"line":875},[872,3768,3346],{"class":886},[872,3770,3771],{"class":925},"ReleaseDeal",[872,3773,3352],{"class":886},[872,3775,3776,3778,3781,3783,3785,3787,3789],{"class":874,"line":783},[872,3777,3357],{"class":886},[872,3779,3780],{"class":925},"DealReleaseReference",[872,3782,3373],{"class":886},[872,3784,3623],{"class":919},[872,3786,3379],{"class":886},[872,3788,3780],{"class":925},[872,3790,3352],{"class":886},[872,3792,3793,3795,3798],{"class":874,"line":916},[872,3794,3357],{"class":886},[872,3796,3797],{"class":925},"Deal",[872,3799,3352],{"class":886},[872,3801,3802,3804,3807],{"class":874,"line":935},[872,3803,3367],{"class":886},[872,3805,3806],{"class":925},"DealTerms",[872,3808,3352],{"class":886},[872,3810,3811,3813,3815,3817,3819,3821,3823],{"class":874,"line":966},[872,3812,3462],{"class":886},[872,3814,3426],{"class":925},[872,3816,3373],{"class":886},[872,3818,3011],{"class":919},[872,3820,3379],{"class":886},[872,3822,3426],{"class":925},[872,3824,3352],{"class":886},[872,3826,3827,3829,3832,3834,3836,3838,3840],{"class":874,"line":972},[872,3828,3462],{"class":886},[872,3830,3831],{"class":925},"ExcludedTerritoryCode",[872,3833,3373],{"class":886},[872,3835,3033],{"class":919},[872,3837,3379],{"class":886},[872,3839,3831],{"class":925},[872,3841,3352],{"class":886},[872,3843,3844,3846,3849,3851,3854,3856,3858],{"class":874,"line":997},[872,3845,3462],{"class":886},[872,3847,3848],{"class":925},"CommercialModelType",[872,3850,3373],{"class":886},[872,3852,3853],{"class":919},"SubscriptionModel",[872,3855,3379],{"class":886},[872,3857,3848],{"class":925},[872,3859,3352],{"class":886},[872,3861,3862,3864,3867],{"class":874,"line":1013},[872,3863,3462],{"class":886},[872,3865,3866],{"class":925},"Usage",[872,3868,3352],{"class":886},[872,3870,3871,3874,3877,3879,3882,3884,3886],{"class":874,"line":1022},[872,3872,3873],{"class":886},"        \u003C",[872,3875,3876],{"class":925},"UseType",[872,3878,3373],{"class":886},[872,3880,3881],{"class":919},"OnDemandStream",[872,3883,3379],{"class":886},[872,3885,3876],{"class":925},[872,3887,3352],{"class":886},[872,3889,3890,3893,3895],{"class":874,"line":1029},[872,3891,3892],{"class":886},"      \u003C/",[872,3894,3866],{"class":925},[872,3896,3352],{"class":886},[872,3898,3899,3901,3904],{"class":874,"line":1043},[872,3900,3462],{"class":886},[872,3902,3903],{"class":925},"ValidityPeriod",[872,3905,3352],{"class":886},[872,3907,3908,3910,3913,3915,3918,3920,3922],{"class":874,"line":1070},[872,3909,3873],{"class":886},[872,3911,3912],{"class":925},"StartDate",[872,3914,3373],{"class":886},[872,3916,3917],{"class":919},"2026-03-15",[872,3919,3379],{"class":886},[872,3921,3912],{"class":925},[872,3923,3352],{"class":886},[872,3925,3926,3928,3930],{"class":874,"line":1098},[872,3927,3892],{"class":886},[872,3929,3903],{"class":925},[872,3931,3352],{"class":886},[872,3933,3934,3936,3938],{"class":874,"line":1103},[872,3935,3481],{"class":886},[872,3937,3806],{"class":925},[872,3939,3352],{"class":886},[872,3941,3942,3944,3946],{"class":874,"line":1126},[872,3943,3388],{"class":886},[872,3945,3797],{"class":925},[872,3947,3352],{"class":886},[872,3949,3950,3952,3954],{"class":874,"line":1132},[872,3951,3379],{"class":886},[872,3953,3771],{"class":925},[872,3955,3352],{"class":886},[2169,3957,3958],{},[746,3959,3960,3963,3964,3967,3968,3972,3973,3975,3976,3979],{},[850,3961,3962],{},"Common DDEX mistake:"," ",[869,3965,3966],{},"DetailsByTerritory"," in ResourceList and ReleaseList describes ",[3969,3970,3971],"em",{},"how content is presented"," in different markets. The ",[869,3974,3752],{}," defines ",[3969,3977,3978],{},"where content is available",". Confusing these two - applying territory restrictions in metadata instead of deals - is one of the most frequent implementation errors.",[858,3981,3983],{"id":3982},"territory-restrictions-in-practice","Territory Restrictions in Practice",[746,3985,3986,3987,1508],{},"When a distributor has agreements with partners who hold rights in specific regions, individual tracks must be excluded from those territories. This requires consistent territory entries across ",[850,3988,3989],{},"three XML sections",[3991,3992,3993,3998,4003],"ol",{},[2204,3994,3995,3997],{},[869,3996,3417],{}," in ResourceList",[2204,3999,4000,4002],{},[869,4001,3654],{}," in ReleaseList",[2204,4004,4005,4007],{},[869,4006,3806],{}," in DealList",[746,4009,4010,4011,4014],{},"Platforms like Revelator handle this at the release level through their UI, but ",[850,4012,4013],{},"track-level territory restrictions within an album"," are where the friction begins. This is exactly the kind of limitation that pushes distributors toward a custom frontend - you model the territory logic in your own system and push the correct per-track restrictions through the API.",[2078,4016],{},[750,4018,4020],{"id":4019},"real-world-gotchas","Real-World Gotchas",[746,4022,4023],{},"Having worked with distributors who use Revelator as their delivery backbone, here are the practical challenges we've encountered:",[2169,4025,4026],{},[746,4027,4028,4031],{},[850,4029,4030],{},"1. Mandatory Human Approval Step","\nRevelator requires a parent account to approve distributions before they reach DSPs. There is no fully automated end-to-end delivery without a human clicking \"approve\" in the web UI. For high-volume operations, this creates a bottleneck that API-only workflows cannot bypass.",[858,4033,4035],{"id":4034},"_2-full-object-re-submission","2. Full Object Re-submission",[746,4037,4038],{},"There is no PATCH support for releases. Editing a release requires re-submitting the full release object. If you omit files, they are auto-deleted. This means your backend must always maintain the complete release state.",[858,4040,4042],{"id":4041},"_3-breaking-api-changes","3. Breaking API Changes",[746,4044,4045],{},"Revelator's API has had several breaking changes in 2025-2026:",[2088,4047,4048,4058],{},[2091,4049,4050],{},[2094,4051,4052,4055],{},[2097,4053,4054],{},"Change",[2097,4056,4057],{},"When",[2104,4059,4060,4075,4086,4094],{},[2094,4061,4062,4072],{},[2109,4063,4064,4065,4068,4069],{},"UPC type changed from ",[869,4066,4067],{},"number"," to ",[869,4070,4071],{},"string",[2109,4073,4074],{},"Feb 2026",[2094,4076,4077,4083],{},[2109,4078,4079,4080],{},"API base URL migrated to ",[869,4081,4082],{},"api.revelator.com",[2109,4084,4085],{},"Mar 2026",[2094,4087,4088,4091],{},[2109,4089,4090],{},"Production/engineering credits became mandatory",[2109,4092,4093],{},"Jun 2025",[2094,4095,4096,4099],{},[2109,4097,4098],{},"Zero-sentinel IDs deprecated",[2109,4100,4101],{},"Nov 2025",[746,4103,4104],{},"Your integration layer needs resilience against schema drift.",[858,4106,4108],{"id":4107},"_4-dsp-specific-ddex-interpretation","4. DSP-Specific DDEX Interpretation",[746,4110,4111],{},"Even when Revelator generates valid ERN XML, each DSP interprets certain elements differently. Spotify's ingestion engine, Apple's Content Provider system, and Amazon's pipeline each have proprietary extensions and quirks. When delivery fails, debugging requires understanding both the DDEX standard and the specific DSP's interpretation.",[858,4113,4115],{"id":4114},"_5-locked-distribution-states","5. Locked Distribution States",[746,4117,4118],{},"Tracks in certain distribution statuses (-10, -11, -20, -21) become read-only. If you need to modify metadata on a locked track, you may need to initiate a takedown and redeliver, adding complexity to your workflow engine.",[2078,4120],{},[750,4122,4124],{"id":4123},"when-does-option-a-become-a-dead-end","When Does Option A Become a Dead End?",[746,4126,4127],{},"The hybrid approach works well when:",[2201,4129,4130,4136,4142,4145],{},[2204,4131,4132,4133],{},"Your catalog is under ",[850,4134,4135],{},"50,000 releases",[2204,4137,4138,4139],{},"You have fewer than ",[850,4140,4141],{},"30 DSP targets",[2204,4143,4144],{},"Territory complexity is moderate (album-level restrictions, not per-track-per-DSP)",[2204,4146,4147],{},"Royalty reporting needs are standard (DSP-level aggregates, not sub-publishing splits)",[746,4149,4150],{},"It starts breaking down when:",[2088,4152,4153,4163],{},[2091,4154,4155],{},[2094,4156,4157,4160],{},[2097,4158,4159],{},"Signal",[2097,4161,4162],{},"Why It Matters",[2104,4164,4165,4175,4185,4195],{},[2094,4166,4167,4172],{},[2109,4168,4169],{},[850,4170,4171],{},"Royalty logic becomes the product",[2109,4173,4174],{},"If your competitive advantage is in how you calculate, split, and report royalties - with advances, recoupables, and multi-party splits - you'll outgrow Revelator's royalty engine before its delivery",[2094,4176,4177,4182],{},[2109,4178,4179],{},[850,4180,4181],{},"You need real-time delivery control",[2109,4183,4184],{},"The mandatory approval step and lack of granular delivery status callbacks limit automation at scale",[2094,4186,4187,4192],{},[2109,4188,4189],{},[850,4190,4191],{},"DSP-specific customization matters",[2109,4193,4194],{},"If you need different DDEX profiles per DSP (ERN 3.8.2 for legacy, ERN 4.3 for Spotify), you'll need your own generation pipeline",[2094,4196,4197,4202],{},[2109,4198,4199],{},[850,4200,4201],{},"Vendor risk becomes unacceptable",[2109,4203,4204],{},"Revelator is VC-backed. API changes, pricing shifts, or an acquisition could force migration under pressure",[2160,4206,4207],{},[746,4208,4209,4210,4213],{},"The practical tipping point is usually ",[850,4211,4212],{},"2-3 years"," into the hybrid approach, when the workarounds and API limitations start costing more in engineering time than building the replaced components would.",[2078,4215],{},[750,4217,4219],{"id":4218},"the-hybrid-to-independent-migration-path","The Hybrid-to-Independent Migration Path",[746,4221,4222],{},"The smartest architecture for Option A anticipates Option B:",[3991,4224,4225,4231,4237,4243],{},[2204,4226,4227,4230],{},[850,4228,4229],{},"Own your metadata."," Never treat Revelator as the source of truth. Your database is canonical; Revelator is a sync target.",[2204,4232,4233,4236],{},[850,4234,4235],{},"Abstract the delivery layer."," Build an internal delivery interface that Revelator implements today but could be swapped for direct DSP integrations tomorrow.",[2204,4238,4239,4242],{},[850,4240,4241],{},"Build your own rights engine from day one."," Territory restrictions, split sheets, and rights ownership are your core IP. Never delegate this logic to the platform.",[2204,4244,4245,4248],{},[850,4246,4247],{},"Invest in DDEX competency."," Understanding ERN generation and validation - even if Revelator handles it today - is essential knowledge for the eventual transition.",[2004,4250,4251],{},[746,4252,4253,4254,4257],{},"This way, when Option A hits its ceiling, you migrate ",[850,4255,4256],{},"component by component"," rather than executing a risky big-bang rewrite.",[2078,4259],{},[750,4261,2016],{"id":2015},[746,4263,4264],{},"For distributors with direct DSP contracts who are hitting the limits of their current platform, the hybrid approach - custom frontend on Revelator's API - is the fastest path to operational control without the multi-year investment of going fully independent.",[746,4266,4267],{},"The key is to build it with migration in mind. Own your metadata, abstract your delivery layer, and invest in DDEX expertise. When the time comes to replace the backend, you'll be swapping a component rather than rebuilding a platform.",[2078,4269],{},[750,4271,4273],{"id":4272},"resources","Resources",[2201,4275,4276,4283,4290,4297,4301,4306],{},[2204,4277,4278],{},[771,4279,4282],{"href":4280,"rel":4281},"https://api-docs.revelator.com",[775],"Revelator API Documentation",[2204,4284,4285],{},[771,4286,4289],{"href":4287,"rel":4288},"https://kb.ddex.net/implementing-each-standard/electronic-release-notification-message-suite-(ern)/",[775],"DDEX ERN Knowledge Base",[2204,4291,4292],{},[771,4293,4296],{"href":4294,"rel":4295},"https://ddexvalidator.musictechlab.io/",[775],"MTL DDEX Validator",[2204,4298,4299],{},[771,4300,136],{"href":137},[2204,4302,4303],{},[771,4304,4305],{"href":161},"Introduction to Generating DDEX Files Using Python",[2204,4307,4308,4311],{},[771,4309,4310],{"href":85},"AI-Powered Analytics Dashboard"," - once distribution data flows into your analytics pipeline, make it queryable with natural language",[2024,4313,4314],{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":782,"searchDepth":783,"depth":783,"links":4316},[4317,4318,4319,4326,4331,4337,4343,4344,4345,4346],{"id":2082,"depth":783,"text":2083},{"id":2154,"depth":783,"text":2155},{"id":2188,"depth":783,"text":2189,"children":4320},[4321,4322,4323,4324,4325],{"id":2198,"depth":916,"text":2199},{"id":2221,"depth":916,"text":2066},{"id":2253,"depth":916,"text":2254},{"id":2268,"depth":916,"text":2269},{"id":2283,"depth":916,"text":2284},{"id":2309,"depth":783,"text":2310,"children":4327},[4328,4329,4330],{"id":2436,"depth":916,"text":2437},{"id":2504,"depth":916,"text":2505},{"id":2560,"depth":916,"text":2561},{"id":3320,"depth":783,"text":3321,"children":4332},[4333,4334,4335,4336],{"id":3330,"depth":916,"text":3331},{"id":3566,"depth":916,"text":3567},{"id":3751,"depth":916,"text":3752},{"id":3982,"depth":916,"text":3983},{"id":4019,"depth":783,"text":4020,"children":4338},[4339,4340,4341,4342],{"id":4034,"depth":916,"text":4035},{"id":4041,"depth":916,"text":4042},{"id":4107,"depth":916,"text":4108},{"id":4114,"depth":916,"text":4115},{"id":4123,"depth":783,"text":4124},{"id":4218,"depth":783,"text":4219},{"id":2015,"depth":783,"text":2016},{"id":4272,"depth":783,"text":4273},"2026-02-17T00:00:00.000Z","A practical guide for music distributors evaluating a hybrid approach: custom frontend with Revelator's API as the delivery backbone. Covers architecture, DDEX integration, territory handling, and when to go fully independent.",{"src":4350},"/images/blog/musictechlab_blog_building-a-custom-music-delivery-platform-on-the-revelator-api.webp",{"enabled":794,"items":4352},[4353,4355,4358,4360],{"text":4354,"icon":2051},"Revelator claims 100+ DSP integrations but requires a manual approval step before delivery.",{"text":4356,"icon":4357},"Territory restrictions must be consistent across three XML sections or deliveries fail silently.","i-lucide-alert-triangle",{"text":4359,"icon":798},"Own your metadata from day one and treat Revelator as a sync target, not the source of truth.",{"text":4361,"icon":4362},"The hybrid approach typically hits its ceiling after 2-3 years of scaling.","i-lucide-trending-up",{},{"title":112,"description":4348},[787,808,4366,4367],"API","royalties","j-0HOY7SF2pZKgaTUKG4xI6pYGGOIVWAd9EiQc5LjGU",{"id":4370,"title":88,"authors":4371,"badge":741,"body":4375,"category":787,"client":741,"date":4379,"description":4380,"extension":790,"faq":741,"featured":69,"featuredOrder":741,"hidden":69,"image":4381,"keyTakeaways":741,"meta":4383,"navigation":794,"path":89,"seo":4384,"status":4385,"stem":90,"tags":4386,"teaser":4390,"__hash__":4391,"score":916},"posts/blog/music-data/ai-rehearsal-spaced-repetition-for-musical-ideas.md",[4372],{"name":4373,"to":2062,"avatar":4374},"Mariusz Smenzyk",{"src":2064},{"type":743,"value":4376,"toc":4377},[],{"title":782,"searchDepth":783,"depth":783,"links":4378},[],"2026-06-01T00:00:00.000Z","What if your voice memos could rehearse themselves back to you at the right moment? Applying spaced repetition — the technique behind language learning apps — to musical ideation.",{"src":4382},"/images/blog/musictechlab_blog_ai-rehearsal.webp",{},{"title":88,"description":4380},"upcoming",[787,4387,4388,4389],"ai","creativity","spaced-repetition","We'll present the concept of AI-powered idea resurfacing — an Anki for music sketches. Nobody is doing this, and we think it has legs.","eulUPi4sL79UUhLrK-kcDT-qHYBKTp5-iYZaYYJjiko",{"id":4393,"title":92,"authors":4394,"badge":741,"body":4397,"category":787,"client":741,"date":4401,"description":4402,"extension":790,"faq":741,"featured":69,"featuredOrder":741,"hidden":69,"image":4403,"keyTakeaways":741,"meta":4405,"navigation":794,"path":93,"seo":4406,"status":4385,"stem":94,"tags":4407,"teaser":4411,"__hash__":4412,"score":916},"posts/blog/music-data/audio-project-organization-mess.md",[4395],{"name":4373,"to":2062,"avatar":4396},{"src":2064},{"type":743,"value":4398,"toc":4399},[],{"title":782,"searchDepth":783,"depth":783,"links":4400},[],"2026-05-10T00:00:00.000Z","Projects, folders, sub-recordings, and versions pile up with no structure. Search and filtering across recordings is missing. No auto-titling or smart tagging — everything is 'Recording 47'.",{"src":4404},"/images/blog/musictechlab_blog_audio-project-organization.webp",{},{"title":92,"description":4402},[787,4408,4409,4410],"audio","organization","workflow","We'll explore why musicians drown in unstructured audio files, why manual tagging always fails, and what an ideal project workspace for music creators would look like.","yzu7hUOUThxdS4j4-pCAyCx6EAS9w_cIJKZhgiIrXCk",1780305202659]