From 0079e63fb0447502ad8e7247f5e79e40edb93e5a Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Mon, 9 Jan 2023 21:35:19 -0700 Subject: [PATCH 01/29] Dockerize all the things --- README.md | 28 ++++---- Zune.DB.Console/Program.cs | 6 +- Zune.Net.Catalog.Image/Dockerfile | 17 +++++ Zune.Net.Catalog.Image/appsettings.json | 2 +- Zune.Net.Catalog/Dockerfile | 17 +++++ Zune.Net.Catalog/appsettings.json | 2 +- .../Controllers/BillingController.cs | 2 +- Zune.Net.Commerce/Dockerfile | 17 +++++ Zune.Net.Commerce/appsettings.json | 4 +- Zune.Net.Inbox/Dockerfile | 17 +++++ Zune.Net.Inbox/appsettings.json | 2 +- Zune.Net.Login/Dockerfile | 17 +++++ Zune.Net.Login/appsettings.json | 2 +- Zune.Net.MetaServices/Dockerfile | 17 +++++ Zune.Net.Mix/Dockerfile | 17 +++++ Zune.Net.SocialApi/Dockerfile | 17 +++++ Zune.Net.SocialApi/appsettings.json | 2 +- Zune.Net.Tiles/Dockerfile | 17 +++++ Zune.Net.Tuners/Dockerfile | 17 +++++ docker-compose.yml | 65 +++++++++++++++++++ 20 files changed, 260 insertions(+), 25 deletions(-) create mode 100644 Zune.Net.Catalog.Image/Dockerfile create mode 100644 Zune.Net.Catalog/Dockerfile create mode 100644 Zune.Net.Commerce/Dockerfile create mode 100644 Zune.Net.Inbox/Dockerfile create mode 100644 Zune.Net.Login/Dockerfile create mode 100644 Zune.Net.MetaServices/Dockerfile create mode 100644 Zune.Net.Mix/Dockerfile create mode 100644 Zune.Net.SocialApi/Dockerfile create mode 100644 Zune.Net.Tiles/Dockerfile create mode 100644 Zune.Net.Tuners/Dockerfile create mode 100644 docker-compose.yml diff --git a/README.md b/README.md index 2a5efc7..9d3dac0 100644 --- a/README.md +++ b/README.md @@ -7,20 +7,20 @@ It is highly advised to use the Community Webservices mod from [Zune Modding Hel If you would rather host the servers yourself, you can add the following to your `hosts` file to redirect all requests made to the Zune.net endpoints to your local servers. Make sure you have also modified each project's `launchSettings.json` to use the correct `applicationUrl`. ``` 127.0.0.1 fai.music.metaservices.microsoft.com -127.0.0.2 catalog.zunes.me -127.0.0.2 catalog-ssl.zunes.me -127.0.0.3 commerce.zunes.me -127.0.0.4 image.catalog.zunes.me -127.0.0.5 socialapi.zunes.me -127.0.0.6 comments.zunes.me -127.0.0.7 inbox.zunes.me -127.0.0.8 mix.zunes.me -127.0.0.9 stats.zunes.me -127.0.0.10 cache-tiles.zunes.me -127.0.0.11 tuners.zunes.me -127.0.0.12 tiles.zunes.me -127.0.0.13 social.zunes.me -127.0.0.14 login.zunes.me +127.0.0.2 catalog.zune.net +127.0.0.2 catalog-ssl.zune.net +127.0.0.3 commerce.zune.net +127.0.0.4 image.catalog.zune.net +127.0.0.5 socialapi.zune.net +127.0.0.6 comments.zune.net +127.0.0.7 inbox.zune.net +127.0.0.8 mix.zune.net +127.0.0.9 stats.zune.net +127.0.0.10 cache-tiles.zune.net +127.0.0.11 tuners.zune.net +127.0.0.12 tiles.zune.net +127.0.0.13 social.zune.net +127.0.0.14 login.zune.net ``` # Unimplemented endpoints diff --git a/Zune.DB.Console/Program.cs b/Zune.DB.Console/Program.cs index c8cafbf..a3667fb 100644 --- a/Zune.DB.Console/Program.cs +++ b/Zune.DB.Console/Program.cs @@ -15,7 +15,7 @@ class Program static async Task Main(string[] args) { // Set up CLI options - string connectionString = "mongodb://localhost:27017"; + string connectionString = "mongodb://mongo:27017"; string dbName = "Zune"; var options = new OptionSet { @@ -60,8 +60,8 @@ static async Task Main(string[] args) Status = "Reviving the Zune social", Bio = "A computer science student at Texas A&M Univserity that can't help but bring back dead Microsoft products.", Location = "College Station, Texas", - UserTile = "http://tiles.zunes.me/tiles/avatar/default.jpg", - Background = "http://tiles.zunes.me/tiles/background/USERBACKGROUND-ART-536X196-49.jpg", + UserTile = "http://tiles.zune.net/tiles/avatar/default.jpg", + Background = "http://tiles.zune.net/tiles/background/USERBACKGROUND-ART-536X196-49.jpg", AcceptedTermsOfService = true, AccountSuspended = false, diff --git a/Zune.Net.Catalog.Image/Dockerfile b/Zune.Net.Catalog.Image/Dockerfile new file mode 100644 index 0000000..b53d90a --- /dev/null +++ b/Zune.Net.Catalog.Image/Dockerfile @@ -0,0 +1,17 @@ +# https://hub.docker.com/_/microsoft-dotnet +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /source + +# copy csproj and restore as distinct layers +COPY ./ . +RUN dotnet restore + +WORKDIR /source/Zune.Net.Catalog.Image +RUN dotnet publish -c release -o /app --no-restore + +# final stage/image +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /app +COPY --from=build /app ./ +ENV DOTNET_EnableDiagnostics=0 +ENTRYPOINT ["dotnet", "Zune.Net.Catalog.Image.dll"] diff --git a/Zune.Net.Catalog.Image/appsettings.json b/Zune.Net.Catalog.Image/appsettings.json index 429f762..7c65aad 100644 --- a/Zune.Net.Catalog.Image/appsettings.json +++ b/Zune.Net.Catalog.Image/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://localhost:27017", + "ConnectionString": "mongodb://mongo:27017", "DatabaseName": "Zune" }, "Logging": { diff --git a/Zune.Net.Catalog/Dockerfile b/Zune.Net.Catalog/Dockerfile new file mode 100644 index 0000000..a981d8f --- /dev/null +++ b/Zune.Net.Catalog/Dockerfile @@ -0,0 +1,17 @@ +# https://hub.docker.com/_/microsoft-dotnet +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /source + +# copy csproj and restore as distinct layers +COPY ./ . +RUN dotnet restore + +WORKDIR /source/Zune.Net.Catalog +RUN dotnet publish -c release -o /app --no-restore + +# final stage/image +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /app +COPY --from=build /app ./ +ENV DOTNET_EnableDiagnostics=0 +ENTRYPOINT ["dotnet", "Zune.Net.Catalog.dll"] diff --git a/Zune.Net.Catalog/appsettings.json b/Zune.Net.Catalog/appsettings.json index 485ec63..31f4816 100644 --- a/Zune.Net.Catalog/appsettings.json +++ b/Zune.Net.Catalog/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://localhost:27017", + "ConnectionString": "mongodb://mongo:27017", "DatabaseName": "Zune" }, "Logging": { diff --git a/Zune.Net.Commerce/Controllers/BillingController.cs b/Zune.Net.Commerce/Controllers/BillingController.cs index 18ca353..d44cf19 100644 --- a/Zune.Net.Commerce/Controllers/BillingController.cs +++ b/Zune.Net.Commerce/Controllers/BillingController.cs @@ -32,7 +32,7 @@ public ActionResult PurchaseHistory() Content = "Purchased something from Chuck Berry", Links = { - new("http://catalog.zunes.me/v3.2/en-US/music/album/06d4ec5e-a1b2-4895-9a09-ca3e8451bcc7") + new("http://catalog.zune.net/v3.2/en-US/music/album/06d4ec5e-a1b2-4895-9a09-ca3e8451bcc7") }, Title = "Oh Baby Doll / Lajaunda (espanol)" } diff --git a/Zune.Net.Commerce/Dockerfile b/Zune.Net.Commerce/Dockerfile new file mode 100644 index 0000000..a981d8f --- /dev/null +++ b/Zune.Net.Commerce/Dockerfile @@ -0,0 +1,17 @@ +# https://hub.docker.com/_/microsoft-dotnet +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /source + +# copy csproj and restore as distinct layers +COPY ./ . +RUN dotnet restore + +WORKDIR /source/Zune.Net.Catalog +RUN dotnet publish -c release -o /app --no-restore + +# final stage/image +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /app +COPY --from=build /app ./ +ENV DOTNET_EnableDiagnostics=0 +ENTRYPOINT ["dotnet", "Zune.Net.Catalog.dll"] diff --git a/Zune.Net.Commerce/appsettings.json b/Zune.Net.Commerce/appsettings.json index 0e21171..c9f8989 100644 --- a/Zune.Net.Commerce/appsettings.json +++ b/Zune.Net.Commerce/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://localhost:27017", + "ConnectionString": "mongodb://mongo:27017", "DatabaseName": "Zune", "MemberCollectionName": "Members" }, @@ -11,7 +11,7 @@ */ "AzureAd": { "Instance": "https://login.microsoftonline.com/", - "Domain": "commerce.zunes.me", + "Domain": "commerce.zune.net", "TenantId": "common", "ClientId": "e7dd59f1-8ea4-49b6-8361-1b10b2551dba", diff --git a/Zune.Net.Inbox/Dockerfile b/Zune.Net.Inbox/Dockerfile new file mode 100644 index 0000000..821f022 --- /dev/null +++ b/Zune.Net.Inbox/Dockerfile @@ -0,0 +1,17 @@ +# https://hub.docker.com/_/microsoft-dotnet +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /source + +# copy csproj and restore as distinct layers +COPY ./ . +RUN dotnet restore + +WORKDIR /source/Zune.Net.Inbox +RUN dotnet publish -c release -o /app --no-restore + +# final stage/image +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /app +COPY --from=build /app ./ +ENV DOTNET_EnableDiagnostics=0 +ENTRYPOINT ["dotnet", "Zune.Net.Inbox.dll"] diff --git a/Zune.Net.Inbox/appsettings.json b/Zune.Net.Inbox/appsettings.json index 38175d4..b4380df 100644 --- a/Zune.Net.Inbox/appsettings.json +++ b/Zune.Net.Inbox/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://localhost:27017", + "ConnectionString": "mongodb://mongo:27017", "DatabaseName": "Zune", "MemberCollectionName": "Members" }, diff --git a/Zune.Net.Login/Dockerfile b/Zune.Net.Login/Dockerfile new file mode 100644 index 0000000..3d79bb0 --- /dev/null +++ b/Zune.Net.Login/Dockerfile @@ -0,0 +1,17 @@ +# https://hub.docker.com/_/microsoft-dotnet +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /source + +# copy csproj and restore as distinct layers +COPY ./ . +RUN dotnet restore + +WORKDIR /source/Zune.Net.Login +RUN dotnet publish -c release -o /app --no-restore + +# final stage/image +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /app +COPY --from=build /app ./ +ENV DOTNET_EnableDiagnostics=0 +ENTRYPOINT ["dotnet", "Zune.Net.Login.dll"] diff --git a/Zune.Net.Login/appsettings.json b/Zune.Net.Login/appsettings.json index 8f33bdc..2458cb6 100644 --- a/Zune.Net.Login/appsettings.json +++ b/Zune.Net.Login/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://localhost:27017", + "ConnectionString": "mongodb://mongo:27017", "DatabaseName": "Zune", }, "Logging": { diff --git a/Zune.Net.MetaServices/Dockerfile b/Zune.Net.MetaServices/Dockerfile new file mode 100644 index 0000000..488ff6b --- /dev/null +++ b/Zune.Net.MetaServices/Dockerfile @@ -0,0 +1,17 @@ +# https://hub.docker.com/_/microsoft-dotnet +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /source + +# copy csproj and restore as distinct layers +COPY ./ . +RUN dotnet restore + +WORKDIR /source/Zune.Net.MetaServices +RUN dotnet publish -c release -o /app --no-restore + +# final stage/image +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /app +COPY --from=build /app ./ +ENV DOTNET_EnableDiagnostics=0 +ENTRYPOINT ["dotnet", "Zune.Net.MetaServices.dll"] diff --git a/Zune.Net.Mix/Dockerfile b/Zune.Net.Mix/Dockerfile new file mode 100644 index 0000000..a8a0596 --- /dev/null +++ b/Zune.Net.Mix/Dockerfile @@ -0,0 +1,17 @@ +# https://hub.docker.com/_/microsoft-dotnet +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /source + +# copy csproj and restore as distinct layers +COPY ./ . +RUN dotnet restore + +WORKDIR /source/Zune.Net.Mix +RUN dotnet publish -c release -o /app --no-restore + +# final stage/image +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /app +COPY --from=build /app ./ +ENV DOTNET_EnableDiagnostics=0 +ENTRYPOINT ["dotnet", "Zune.Net.Mix.dll"] diff --git a/Zune.Net.SocialApi/Dockerfile b/Zune.Net.SocialApi/Dockerfile new file mode 100644 index 0000000..d1a5f3d --- /dev/null +++ b/Zune.Net.SocialApi/Dockerfile @@ -0,0 +1,17 @@ +# https://hub.docker.com/_/microsoft-dotnet +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /source + +# copy csproj and restore as distinct layers +COPY ./ . +RUN dotnet restore + +WORKDIR /source/Zune.Net.SocialApi +RUN dotnet publish -c release -o /app --no-restore + +# final stage/image +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /app +COPY --from=build /app ./ +ENV DOTNET_EnableDiagnostics=0 +ENTRYPOINT ["dotnet", "Zune.Net.SocialApi.dll"] diff --git a/Zune.Net.SocialApi/appsettings.json b/Zune.Net.SocialApi/appsettings.json index 38175d4..b4380df 100644 --- a/Zune.Net.SocialApi/appsettings.json +++ b/Zune.Net.SocialApi/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://localhost:27017", + "ConnectionString": "mongodb://mongo:27017", "DatabaseName": "Zune", "MemberCollectionName": "Members" }, diff --git a/Zune.Net.Tiles/Dockerfile b/Zune.Net.Tiles/Dockerfile new file mode 100644 index 0000000..f8e6249 --- /dev/null +++ b/Zune.Net.Tiles/Dockerfile @@ -0,0 +1,17 @@ +# https://hub.docker.com/_/microsoft-dotnet +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /source + +# copy csproj and restore as distinct layers +COPY ./ . +RUN dotnet restore + +WORKDIR /source/Zune.Net.Tiles +RUN dotnet publish -c release -o /app --no-restore + +# final stage/image +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /app +COPY --from=build /app ./ +ENV DOTNET_EnableDiagnostics=0 +ENTRYPOINT ["dotnet", "Zune.Net.Tiles.dll"] diff --git a/Zune.Net.Tuners/Dockerfile b/Zune.Net.Tuners/Dockerfile new file mode 100644 index 0000000..d999867 --- /dev/null +++ b/Zune.Net.Tuners/Dockerfile @@ -0,0 +1,17 @@ +# https://hub.docker.com/_/microsoft-dotnet +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +WORKDIR /source + +# copy csproj and restore as distinct layers +COPY ./ . +RUN dotnet restore + +WORKDIR /source/Zune.Net.Tuners +RUN dotnet publish -c release -o /app --no-restore + +# final stage/image +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /app +COPY --from=build /app ./ +ENV DOTNET_EnableDiagnostics=0 +ENTRYPOINT ["dotnet", "Zune.Net.Tuners.dll"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5527812 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,65 @@ +version: '3.4' + +services: + mongodb: + image: mongo:latest + environment: + MONGO_INITDB_ROOT_USERNAME: root + MONGO_INITDB_ROOT_PASSWORD: rootpassword + ports: + - 27017:27017 + volumes: + - mongodb_data_container:/data/db + + catalog: + build: + context: ./ + dockerfile: Zune.Net.Catalog/Dockerfile + + catalog.image: + build: + context: ./ + dockerfile: Zune.Net.Catalog.Image/Dockerfile + + commerce: + build: + context: ./ + dockerfile: Zune.Net.Commerce/Dockerfile + + inbox: + build: + context: ./ + dockerfile: Zune.Net.Inbox/Dockerfile + + login: + build: + context: ./ + dockerfile: Zune.Net.Login/Dockerfile + + metaservices: + build: + context: ./ + dockerfile: Zune.Net.MetaServices/Dockerfile + + mix: + build: + context: ./ + dockerfile: Zune.Net.Mix/Dockerfile + + social: + build: + context: ./ + dockerfile: Zune.Net.SocialApi/Dockerfile + + tiles: + build: + context: ./ + dockerfile: Zune.Net.Tiles/Dockerfile + + tuners: + build: + context: ./ + dockerfile: Zune.Net.Tuners/Dockerfile + +volumes: + mongodb_data_container: \ No newline at end of file From 974505dbfb357ae43ad8bf0ebf40e5756a7202a0 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Mon, 9 Jan 2023 21:41:43 -0700 Subject: [PATCH 02/29] Oops, too greedy with the zune.net rewrite --- .gitignore | 2 ++ README.md | 28 +++++++++---------- Zune.DB.Console/Program.cs | 4 +-- .../Controllers/BillingController.cs | 2 +- .../Controllers/MessagingInboxController.cs | 11 ++++---- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 8e5c6e6..6b0b210 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +*/DevConstants.cs + # User-specific files *.rsuser *.suo diff --git a/README.md b/README.md index 9d3dac0..2a5efc7 100644 --- a/README.md +++ b/README.md @@ -7,20 +7,20 @@ It is highly advised to use the Community Webservices mod from [Zune Modding Hel If you would rather host the servers yourself, you can add the following to your `hosts` file to redirect all requests made to the Zune.net endpoints to your local servers. Make sure you have also modified each project's `launchSettings.json` to use the correct `applicationUrl`. ``` 127.0.0.1 fai.music.metaservices.microsoft.com -127.0.0.2 catalog.zune.net -127.0.0.2 catalog-ssl.zune.net -127.0.0.3 commerce.zune.net -127.0.0.4 image.catalog.zune.net -127.0.0.5 socialapi.zune.net -127.0.0.6 comments.zune.net -127.0.0.7 inbox.zune.net -127.0.0.8 mix.zune.net -127.0.0.9 stats.zune.net -127.0.0.10 cache-tiles.zune.net -127.0.0.11 tuners.zune.net -127.0.0.12 tiles.zune.net -127.0.0.13 social.zune.net -127.0.0.14 login.zune.net +127.0.0.2 catalog.zunes.me +127.0.0.2 catalog-ssl.zunes.me +127.0.0.3 commerce.zunes.me +127.0.0.4 image.catalog.zunes.me +127.0.0.5 socialapi.zunes.me +127.0.0.6 comments.zunes.me +127.0.0.7 inbox.zunes.me +127.0.0.8 mix.zunes.me +127.0.0.9 stats.zunes.me +127.0.0.10 cache-tiles.zunes.me +127.0.0.11 tuners.zunes.me +127.0.0.12 tiles.zunes.me +127.0.0.13 social.zunes.me +127.0.0.14 login.zunes.me ``` # Unimplemented endpoints diff --git a/Zune.DB.Console/Program.cs b/Zune.DB.Console/Program.cs index a3667fb..bfc96d0 100644 --- a/Zune.DB.Console/Program.cs +++ b/Zune.DB.Console/Program.cs @@ -60,8 +60,8 @@ static async Task Main(string[] args) Status = "Reviving the Zune social", Bio = "A computer science student at Texas A&M Univserity that can't help but bring back dead Microsoft products.", Location = "College Station, Texas", - UserTile = "http://tiles.zune.net/tiles/avatar/default.jpg", - Background = "http://tiles.zune.net/tiles/background/USERBACKGROUND-ART-536X196-49.jpg", + UserTile = "http://tiles.zunes.me/tiles/avatar/default.jpg", + Background = "http://tiles.zunes.me/tiles/background/USERBACKGROUND-ART-536X196-49.jpg", AcceptedTermsOfService = true, AccountSuspended = false, diff --git a/Zune.Net.Commerce/Controllers/BillingController.cs b/Zune.Net.Commerce/Controllers/BillingController.cs index d44cf19..18ca353 100644 --- a/Zune.Net.Commerce/Controllers/BillingController.cs +++ b/Zune.Net.Commerce/Controllers/BillingController.cs @@ -32,7 +32,7 @@ public ActionResult PurchaseHistory() Content = "Purchased something from Chuck Berry", Links = { - new("http://catalog.zune.net/v3.2/en-US/music/album/06d4ec5e-a1b2-4895-9a09-ca3e8451bcc7") + new("http://catalog.zunes.me/v3.2/en-US/music/album/06d4ec5e-a1b2-4895-9a09-ca3e8451bcc7") }, Title = "Oh Baby Doll / Lajaunda (espanol)" } diff --git a/Zune.Net.Inbox/Controllers/MessagingInboxController.cs b/Zune.Net.Inbox/Controllers/MessagingInboxController.cs index cd526ae..02ebee1 100644 --- a/Zune.Net.Inbox/Controllers/MessagingInboxController.cs +++ b/Zune.Net.Inbox/Controllers/MessagingInboxController.cs @@ -68,12 +68,13 @@ public ActionResult Details(string locale, string zuneTag, strin [HttpGet] public IActionResult UnreadCont(string locale, string zuneTag) { - using var ctx = new ZuneNetContext(); - Member member = ctx.Members.First(m => m.ZuneTag == zuneTag); - if (member == null) - return StatusCode(StatusCodes.Status400BadRequest, $"User {zuneTag} does not exist."); + // using var ctx = new ZuneNetContext(); + // Member member = ctx.Members.First(m => m.ZuneTag == zuneTag); + // if (member == null) + // return StatusCode(StatusCodes.Status400BadRequest, $"User {zuneTag} does not exist."); - return Content(member.Messages.Count(msg => !msg.IsRead).ToString()); + // return Content(member.Messages.Count(msg => !msg.IsRead).ToString()); + return Content("1"); } } } From 8a8befc5a30962950c9c88bcc5a7c9f5ce6a9d04 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Tue, 10 Jan 2023 20:15:17 -0700 Subject: [PATCH 03/29] got networking up and running --- .dockerignore | 5 +- Zune.Net.Catalog.Image/Dockerfile | 3 +- Zune.Net.Catalog/Dockerfile | 3 +- Zune.Net.Commerce/Dockerfile | 1 + Zune.Net.Inbox/Dockerfile | 1 + Zune.Net.Login/Dockerfile | 1 + Zune.Net.MetaServices/Dockerfile | 1 + Zune.Net.Mix/Dockerfile | 1 + Zune.Net.SocialApi/Dockerfile | 1 + Zune.Net.Tiles/Dockerfile | 1 + Zune.Net.Tuners/Dockerfile | 1 + docker-compose.yml | 70 +++++++++++++++- nginx/Dockerfile | 3 + nginx/nginx.conf | 129 ++++++++++++++++++++++++++++++ 14 files changed, 214 insertions(+), 7 deletions(-) create mode 100644 nginx/Dockerfile create mode 100644 nginx/nginx.conf diff --git a/.dockerignore b/.dockerignore index 3729ff0..efcde58 100644 --- a/.dockerignore +++ b/.dockerignore @@ -22,4 +22,7 @@ **/secrets.dev.yaml **/values.dev.yaml LICENSE -README.md \ No newline at end of file +README.md + +#ignore nginx so loadbalancer changes don't trigger a rebuild of the rest of the application +/nginx diff --git a/Zune.Net.Catalog.Image/Dockerfile b/Zune.Net.Catalog.Image/Dockerfile index b53d90a..5031045 100644 --- a/Zune.Net.Catalog.Image/Dockerfile +++ b/Zune.Net.Catalog.Image/Dockerfile @@ -2,9 +2,10 @@ FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build WORKDIR /source -# copy csproj and restore as distinct layers +# cache the build result to speed up subsequent package steps COPY ./ . RUN dotnet restore +RUN dotnet build --no-restore WORKDIR /source/Zune.Net.Catalog.Image RUN dotnet publish -c release -o /app --no-restore diff --git a/Zune.Net.Catalog/Dockerfile b/Zune.Net.Catalog/Dockerfile index a981d8f..15bf685 100644 --- a/Zune.Net.Catalog/Dockerfile +++ b/Zune.Net.Catalog/Dockerfile @@ -2,9 +2,10 @@ FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build WORKDIR /source -# copy csproj and restore as distinct layers +# cache the build result to speed up subsequent package steps COPY ./ . RUN dotnet restore +RUN dotnet build --no-restore WORKDIR /source/Zune.Net.Catalog RUN dotnet publish -c release -o /app --no-restore diff --git a/Zune.Net.Commerce/Dockerfile b/Zune.Net.Commerce/Dockerfile index a981d8f..ef9a908 100644 --- a/Zune.Net.Commerce/Dockerfile +++ b/Zune.Net.Commerce/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore +RUN dotnet build --no-restore WORKDIR /source/Zune.Net.Catalog RUN dotnet publish -c release -o /app --no-restore diff --git a/Zune.Net.Inbox/Dockerfile b/Zune.Net.Inbox/Dockerfile index 821f022..ae89b00 100644 --- a/Zune.Net.Inbox/Dockerfile +++ b/Zune.Net.Inbox/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore +RUN dotnet build --no-restore WORKDIR /source/Zune.Net.Inbox RUN dotnet publish -c release -o /app --no-restore diff --git a/Zune.Net.Login/Dockerfile b/Zune.Net.Login/Dockerfile index 3d79bb0..bae9893 100644 --- a/Zune.Net.Login/Dockerfile +++ b/Zune.Net.Login/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore +RUN dotnet build --no-restore WORKDIR /source/Zune.Net.Login RUN dotnet publish -c release -o /app --no-restore diff --git a/Zune.Net.MetaServices/Dockerfile b/Zune.Net.MetaServices/Dockerfile index 488ff6b..2c5c07b 100644 --- a/Zune.Net.MetaServices/Dockerfile +++ b/Zune.Net.MetaServices/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore +RUN dotnet build --no-restore WORKDIR /source/Zune.Net.MetaServices RUN dotnet publish -c release -o /app --no-restore diff --git a/Zune.Net.Mix/Dockerfile b/Zune.Net.Mix/Dockerfile index a8a0596..6315b33 100644 --- a/Zune.Net.Mix/Dockerfile +++ b/Zune.Net.Mix/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore +RUN dotnet build --no-restore WORKDIR /source/Zune.Net.Mix RUN dotnet publish -c release -o /app --no-restore diff --git a/Zune.Net.SocialApi/Dockerfile b/Zune.Net.SocialApi/Dockerfile index d1a5f3d..e818394 100644 --- a/Zune.Net.SocialApi/Dockerfile +++ b/Zune.Net.SocialApi/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore +RUN dotnet build --no-restore WORKDIR /source/Zune.Net.SocialApi RUN dotnet publish -c release -o /app --no-restore diff --git a/Zune.Net.Tiles/Dockerfile b/Zune.Net.Tiles/Dockerfile index f8e6249..9dda067 100644 --- a/Zune.Net.Tiles/Dockerfile +++ b/Zune.Net.Tiles/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore +RUN dotnet build --no-restore WORKDIR /source/Zune.Net.Tiles RUN dotnet publish -c release -o /app --no-restore diff --git a/Zune.Net.Tuners/Dockerfile b/Zune.Net.Tuners/Dockerfile index d999867..96274e4 100644 --- a/Zune.Net.Tuners/Dockerfile +++ b/Zune.Net.Tuners/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore +RUN dotnet build --no-restore WORKDIR /source/Zune.Net.Tuners RUN dotnet publish -c release -o /app --no-restore diff --git a/docker-compose.yml b/docker-compose.yml index 5527812..4a86556 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,9 @@ version: '3.4' +networks: + backend: + name: backend + services: mongodb: image: mongo:latest @@ -10,56 +14,114 @@ services: - 27017:27017 volumes: - mongodb_data_container:/data/db + networks: + - backend + + nginx: + build: + context: nginx + ports: + - 81:81 + networks: + - backend + - default catalog: build: context: ./ dockerfile: Zune.Net.Catalog/Dockerfile + depends_on: + - mongodb + - nginx + networks: + - backend catalog.image: build: context: ./ dockerfile: Zune.Net.Catalog.Image/Dockerfile + depends_on: + - mongodb + - nginx + networks: + - backend commerce: build: context: ./ dockerfile: Zune.Net.Commerce/Dockerfile + depends_on: + - mongodb + - nginx + networks: + - backend - inbox: - build: - context: ./ - dockerfile: Zune.Net.Inbox/Dockerfile + # inbox: + # build: + # context: ./ + # dockerfile: Zune.Net.Inbox/Dockerfile + # ports: + # - 80:8083 login: build: context: ./ dockerfile: Zune.Net.Login/Dockerfile + depends_on: + - mongodb + - nginx + networks: + - backend metaservices: build: context: ./ dockerfile: Zune.Net.MetaServices/Dockerfile + depends_on: + - mongodb + - nginx + networks: + - backend mix: build: context: ./ dockerfile: Zune.Net.Mix/Dockerfile + depends_on: + - mongodb + - nginx + networks: + - backend social: build: context: ./ dockerfile: Zune.Net.SocialApi/Dockerfile + depends_on: + - mongodb + - nginx + networks: + - backend tiles: build: context: ./ dockerfile: Zune.Net.Tiles/Dockerfile + depends_on: + - mongodb + - nginx + networks: + - backend tuners: build: context: ./ dockerfile: Zune.Net.Tuners/Dockerfile + depends_on: + - mongodb + - nginx + networks: + - backend volumes: mongodb_data_container: \ No newline at end of file diff --git a/nginx/Dockerfile b/nginx/Dockerfile new file mode 100644 index 0000000..9c54268 --- /dev/null +++ b/nginx/Dockerfile @@ -0,0 +1,3 @@ +FROM nginx:alpine + +COPY nginx.conf /etc/nginx/nginx.conf \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..475e000 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,129 @@ +worker_processes 1; + +events { worker_connections 1024; } + +http { + + sendfile on; + + # upstream catalog { + # server catalog:8080; + # } + + server { + listen 81; + server_name catalog.zune.net; + location / { + proxy_pass http://catalog; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } + + server { + listen 81; + server_name image.catalog.zune.net; + location / { + proxy_pass http://catalog.image; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } + + server { + listen 81; + server_name commerce.zune.net; + location / { + proxy_pass http://commerce; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } + + server { + listen 81; + server_name inbox.zune.net; + location / { + proxy_pass http://inbox; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } + + server { + listen 81; + server_name mix.zune.net; + location / { + proxy_pass http://mix; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } + + server { + listen 81; + server_name tuners.zune.net; + location / { + proxy_pass http://tuners; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } + + server { + listen 81; + server_name tiles.zune.net; + location / { + proxy_pass http://tiles; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } + + server { + listen 81; + server_name social.zune.net; + location / { + proxy_pass http://social; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } + + server { + listen 81; + server_name login.zune.net; + location / { + proxy_pass http://login; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } +} \ No newline at end of file From 9749fdbcf04e5c4de3c3568739842ad4e7a9ab9c Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Tue, 10 Jan 2023 20:54:40 -0700 Subject: [PATCH 04/29] get the whole stack working with the zune HD --- Zune.DB.Console/Program.cs | 2 +- .../Controllers/ImageController.cs | 2 +- .../Controllers/Music/AlbumController.cs | 2 +- .../Controllers/Music/ArtistController.cs | 2 +- .../Controllers/Music/ChartController.cs | 2 +- .../Controllers/Music/FeaturesController.cs | 2 +- .../Controllers/Music/GenreController.cs | 2 +- .../Controllers/Music/TrackController.cs | 2 +- .../Controllers/Podcast/ChartController.cs | 2 +- .../Controllers/Podcast/PodcastController.cs | 2 +- docker-compose.yml | 23 ++++++++++++------- nginx/nginx.conf | 18 +++++++-------- 12 files changed, 34 insertions(+), 27 deletions(-) diff --git a/Zune.DB.Console/Program.cs b/Zune.DB.Console/Program.cs index bfc96d0..2de4495 100644 --- a/Zune.DB.Console/Program.cs +++ b/Zune.DB.Console/Program.cs @@ -15,7 +15,7 @@ class Program static async Task Main(string[] args) { // Set up CLI options - string connectionString = "mongodb://mongo:27017"; + string connectionString = "mongodb://root:rootpassword@192.168.1.2:27017"; string dbName = "Zune"; var options = new OptionSet { diff --git a/Zune.Net.Catalog.Image/Controllers/ImageController.cs b/Zune.Net.Catalog.Image/Controllers/ImageController.cs index aac66ef..f81c6e7 100644 --- a/Zune.Net.Catalog.Image/Controllers/ImageController.cs +++ b/Zune.Net.Catalog.Image/Controllers/ImageController.cs @@ -11,7 +11,7 @@ namespace Zune.Net.Catalog.Image.Controllers { - [Route("/v3.2/{culture}/")] + [Route("/v{version:decimal}/{culture}/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ImageController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Music/AlbumController.cs b/Zune.Net.Catalog/Controllers/Music/AlbumController.cs index 42ae944..e6174be 100644 --- a/Zune.Net.Catalog/Controllers/Music/AlbumController.cs +++ b/Zune.Net.Catalog/Controllers/Music/AlbumController.cs @@ -6,7 +6,7 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v3.2/{culture}/music/album/")] + [Route("/v{version:decimal}/{culture}/music/album/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class AlbumController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Music/ArtistController.cs b/Zune.Net.Catalog/Controllers/Music/ArtistController.cs index 9fb85f1..ae6c095 100644 --- a/Zune.Net.Catalog/Controllers/Music/ArtistController.cs +++ b/Zune.Net.Catalog/Controllers/Music/ArtistController.cs @@ -15,7 +15,7 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v3.2/{culture}/music/artist/")] + [Route("/v{version:decimal}/{culture}/music/artist/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ArtistController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Music/ChartController.cs b/Zune.Net.Catalog/Controllers/Music/ChartController.cs index 1d83a88..db864df 100644 --- a/Zune.Net.Catalog/Controllers/Music/ChartController.cs +++ b/Zune.Net.Catalog/Controllers/Music/ChartController.cs @@ -8,7 +8,7 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v3.2/{culture}/music/chart/zune/")] + [Route("/v{version:decimal}/{culture}/music/chart/zune/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ChartController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs b/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs index d0b49ff..38eecc9 100644 --- a/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs +++ b/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs @@ -6,7 +6,7 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v3.2/{culture}/music/features/")] + [Route("/v{version:decimal}/{culture}/music/features/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class FeaturesController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Music/GenreController.cs b/Zune.Net.Catalog/Controllers/Music/GenreController.cs index 72cfcf5..9ebbd89 100644 --- a/Zune.Net.Catalog/Controllers/Music/GenreController.cs +++ b/Zune.Net.Catalog/Controllers/Music/GenreController.cs @@ -6,7 +6,7 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v3.2/{culture}/music/genre/")] + [Route("/v{version:decimal}/{culture}/music/genre/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class GenreController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Music/TrackController.cs b/Zune.Net.Catalog/Controllers/Music/TrackController.cs index b7806f5..f853649 100644 --- a/Zune.Net.Catalog/Controllers/Music/TrackController.cs +++ b/Zune.Net.Catalog/Controllers/Music/TrackController.cs @@ -9,7 +9,7 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v3.2/{culture}/music/track/")] + [Route("/v{version:decimal}/{culture}/music/track/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class TrackController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs b/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs index 9d2cb68..7217bfc 100644 --- a/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs +++ b/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs @@ -7,7 +7,7 @@ namespace Zune.Net.Catalog.Controllers.Podcast { - [Route("/v3.2/{culture}/podcastchart/zune/")] + [Route("/v{version:decimal}/{culture}/podcastchart/zune/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ChartController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs b/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs index c8b546e..47c25c2 100644 --- a/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs +++ b/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs @@ -9,7 +9,7 @@ namespace Zune.Net.Catalog.Controllers.Podcast { - [Route("/v3.2/{culture}/")] + [Route("/v{version:decimal}/{culture}/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class PodcastController : Controller { diff --git a/docker-compose.yml b/docker-compose.yml index 4a86556..27d2e9c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,9 @@ version: '3.4' networks: backend: name: backend + zune.net: + name: zune.net + driver: bridge services: mongodb: @@ -16,15 +19,16 @@ services: - mongodb_data_container:/data/db networks: - backend + - zune.net nginx: build: context: nginx ports: - - 81:81 + - 80:80 networks: - backend - - default + - zune.net catalog: build: @@ -56,12 +60,15 @@ services: networks: - backend - # inbox: - # build: - # context: ./ - # dockerfile: Zune.Net.Inbox/Dockerfile - # ports: - # - 80:8083 + inbox: + build: + context: ./ + dockerfile: Zune.Net.Inbox/Dockerfile + depends_on: + - mongodb + - nginx + networks: + - backend login: build: diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 475e000..323f4a0 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -11,7 +11,7 @@ http { # } server { - listen 81; + listen 80; server_name catalog.zune.net; location / { proxy_pass http://catalog; @@ -24,7 +24,7 @@ http { } server { - listen 81; + listen 80; server_name image.catalog.zune.net; location / { proxy_pass http://catalog.image; @@ -37,7 +37,7 @@ http { } server { - listen 81; + listen 80; server_name commerce.zune.net; location / { proxy_pass http://commerce; @@ -50,7 +50,7 @@ http { } server { - listen 81; + listen 80; server_name inbox.zune.net; location / { proxy_pass http://inbox; @@ -63,7 +63,7 @@ http { } server { - listen 81; + listen 80; server_name mix.zune.net; location / { proxy_pass http://mix; @@ -76,7 +76,7 @@ http { } server { - listen 81; + listen 80; server_name tuners.zune.net; location / { proxy_pass http://tuners; @@ -89,7 +89,7 @@ http { } server { - listen 81; + listen 80; server_name tiles.zune.net; location / { proxy_pass http://tiles; @@ -102,7 +102,7 @@ http { } server { - listen 81; + listen 80; server_name social.zune.net; location / { proxy_pass http://social; @@ -115,7 +115,7 @@ http { } server { - listen 81; + listen 80; server_name login.zune.net; location / { proxy_pass http://login; From a037658ff92b0a1985e8e1357b3b1f060bcbe46e Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Tue, 10 Jan 2023 21:33:28 -0700 Subject: [PATCH 05/29] get the resources into the publish output --- Zune.Net.Tuners/Zune.Net.Tuners.csproj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Zune.Net.Tuners/Zune.Net.Tuners.csproj b/Zune.Net.Tuners/Zune.Net.Tuners.csproj index 9b8a345..e1c242d 100644 --- a/Zune.Net.Tuners/Zune.Net.Tuners.csproj +++ b/Zune.Net.Tuners/Zune.Net.Tuners.csproj @@ -6,8 +6,10 @@ enable - - + + + PreserveNewest + Always + - From 2803257aee8fdf721e9b8d9e3dce18b50663751b Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Tue, 10 Jan 2023 21:33:53 -0700 Subject: [PATCH 06/29] lets pretend we are actually zune.net for a little bit --- Zune.DB.Console/Program.cs | 4 ++-- Zune.Net.Commerce/Controllers/BillingController.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Zune.DB.Console/Program.cs b/Zune.DB.Console/Program.cs index 2de4495..35874b2 100644 --- a/Zune.DB.Console/Program.cs +++ b/Zune.DB.Console/Program.cs @@ -60,8 +60,8 @@ static async Task Main(string[] args) Status = "Reviving the Zune social", Bio = "A computer science student at Texas A&M Univserity that can't help but bring back dead Microsoft products.", Location = "College Station, Texas", - UserTile = "http://tiles.zunes.me/tiles/avatar/default.jpg", - Background = "http://tiles.zunes.me/tiles/background/USERBACKGROUND-ART-536X196-49.jpg", + UserTile = "http://tiles.zune.net/tiles/avatar/default.jpg", + Background = "http://tiles.zune.net/tiles/background/USERBACKGROUND-ART-536X196-49.jpg", AcceptedTermsOfService = true, AccountSuspended = false, diff --git a/Zune.Net.Commerce/Controllers/BillingController.cs b/Zune.Net.Commerce/Controllers/BillingController.cs index 18ca353..d44cf19 100644 --- a/Zune.Net.Commerce/Controllers/BillingController.cs +++ b/Zune.Net.Commerce/Controllers/BillingController.cs @@ -32,7 +32,7 @@ public ActionResult PurchaseHistory() Content = "Purchased something from Chuck Berry", Links = { - new("http://catalog.zunes.me/v3.2/en-US/music/album/06d4ec5e-a1b2-4895-9a09-ca3e8451bcc7") + new("http://catalog.zune.net/v3.2/en-US/music/album/06d4ec5e-a1b2-4895-9a09-ca3e8451bcc7") }, Title = "Oh Baby Doll / Lajaunda (espanol)" } From 9160bb9442cf0ad5593c124b7195cee00d9b754c Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Tue, 10 Jan 2023 22:56:52 -0700 Subject: [PATCH 07/29] a couple more docker settings --- Zune.Net.Catalog.Image/appsettings.json | 2 +- Zune.Net.Catalog/appsettings.json | 2 +- Zune.Net.Commerce/appsettings.json | 2 +- Zune.Net.Inbox/appsettings.json | 2 +- Zune.Net.Login/appsettings.json | 2 +- Zune.Net.SocialApi/appsettings.json | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Zune.Net.Catalog.Image/appsettings.json b/Zune.Net.Catalog.Image/appsettings.json index 7c65aad..14586bc 100644 --- a/Zune.Net.Catalog.Image/appsettings.json +++ b/Zune.Net.Catalog.Image/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongo:27017", + "ConnectionString": "mongodb://mongodb:27017", "DatabaseName": "Zune" }, "Logging": { diff --git a/Zune.Net.Catalog/appsettings.json b/Zune.Net.Catalog/appsettings.json index 31f4816..5c3a13e 100644 --- a/Zune.Net.Catalog/appsettings.json +++ b/Zune.Net.Catalog/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongo:27017", + "ConnectionString": "mongodb://mongodb:27017", "DatabaseName": "Zune" }, "Logging": { diff --git a/Zune.Net.Commerce/appsettings.json b/Zune.Net.Commerce/appsettings.json index c9f8989..9ad180b 100644 --- a/Zune.Net.Commerce/appsettings.json +++ b/Zune.Net.Commerce/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongo:27017", + "ConnectionString": "mongodb://mongodb:27017", "DatabaseName": "Zune", "MemberCollectionName": "Members" }, diff --git a/Zune.Net.Inbox/appsettings.json b/Zune.Net.Inbox/appsettings.json index b4380df..cffac35 100644 --- a/Zune.Net.Inbox/appsettings.json +++ b/Zune.Net.Inbox/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongo:27017", + "ConnectionString": "mongodb://mongodb:27017", "DatabaseName": "Zune", "MemberCollectionName": "Members" }, diff --git a/Zune.Net.Login/appsettings.json b/Zune.Net.Login/appsettings.json index 2458cb6..ac2b7d4 100644 --- a/Zune.Net.Login/appsettings.json +++ b/Zune.Net.Login/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongo:27017", + "ConnectionString": "mongodb://mongodb:27017", "DatabaseName": "Zune", }, "Logging": { diff --git a/Zune.Net.SocialApi/appsettings.json b/Zune.Net.SocialApi/appsettings.json index b4380df..cffac35 100644 --- a/Zune.Net.SocialApi/appsettings.json +++ b/Zune.Net.SocialApi/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongo:27017", + "ConnectionString": "mongodb://mongodb:27017", "DatabaseName": "Zune", "MemberCollectionName": "Members" }, From d22276a7b3795dcf477674176b80c0b1344b9eac Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Tue, 10 Jan 2023 22:57:20 -0700 Subject: [PATCH 08/29] this doesnt work, but maybe its a starting point --- .../Controllers/ImageController.cs | 21 ++++++++++++------- Zune.Net.Shared/Helpers/MusicBrainz.Album.cs | 10 +++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Zune.Net.Catalog.Image/Controllers/ImageController.cs b/Zune.Net.Catalog.Image/Controllers/ImageController.cs index f81c6e7..9873800 100644 --- a/Zune.Net.Catalog.Image/Controllers/ImageController.cs +++ b/Zune.Net.Catalog.Image/Controllers/ImageController.cs @@ -73,23 +73,28 @@ public async Task Image(Guid id) : NotFound(); } - [HttpGet, Route("music/artist/{id}/{type}")] - public async Task ArtistImage(string id, string type) + [HttpGet, Route("music/{imageKind}/{id}/{type}")] + public async Task ArtistImage(Guid id, string type) { - string? imageUrl = null; + Uri? imageUrl = null; if (type == "primaryImage") { - Guid mbid = Guid.Parse(id); - (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); + (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(id); - imageUrl = dc_artist.Value("images")? + imageUrl = new Uri(dc_artist.Value("images")? .FirstOrDefault(i => i.Value("type") == "primary")? - .Value("uri"); + .Value("uri")); + } + if(type == "albumImage") + { + imageUrl = MusicBrainz.GetAlbumArtByMBID(id); + // var imageId = album.Images.ToArray().First().Id; + } return imageUrl != null - ? Redirect(imageUrl) + ? Redirect(imageUrl.AbsoluteUri) : NotFound(); } } diff --git a/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs b/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs index ad6b6be..1b8fe7a 100644 --- a/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs +++ b/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs @@ -34,6 +34,16 @@ public static Album GetAlbumByMBID(Guid mbid) return MBReleaseToAlbum(mb_rel); } + public static Uri GetAlbumArtByMBID(Guid mbid) + { + var mb_rel = _query.LookupRelease(mbid, Include.Genres | Include.ArtistCredits | Include.Recordings | Include.Media); + if(mb_rel.CoverArtArchive.Front) + { + new Uri($"http://coverartarchive.org/release/{mbid}/front"); + } + return null; + } + public static Album MBReleaseToAlbum(IRelease mb_rel, DateTime? updated = null, bool includeRights = true) { From 23aa5417c9da4ac2014e890ba6f8bab44f95c643 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Fri, 17 Feb 2023 16:10:06 -0700 Subject: [PATCH 09/29] Somehow, this revives a bunch of the commerce endpoints. You'll have to recover your own SID from the POST and add it to your profile to use it though. --- Zune.DB.Console/Program.cs | 139 +++++++----------- Zune.DB/Helpers.cs | 36 +++-- Zune.DB/Models/Member.cs | 4 + Zune.DB/Models/Tuner.cs | 3 +- Zune.DB/ZuneNetContext.cs | 14 ++ .../Controllers/ImageController.cs | 76 ++++++++-- Zune.Net.Catalog.Image/Program.cs | 7 + .../appsettings.Development.json | 3 +- Zune.Net.Catalog.Image/appsettings.json | 2 +- Zune.Net.Catalog/appsettings.Development.json | 4 +- Zune.Net.Catalog/appsettings.json | 2 +- Zune.Net.Commerce/Config.cs | 46 ------ .../Controllers/AccountController.cs | 101 +++++++++++-- .../Controllers/BillingController.cs | 4 +- .../Controllers/TunerController.cs | 47 ++++++ Zune.Net.Commerce/Dockerfile | 4 +- Zune.Net.Commerce/Helpers/RequestHelpers.cs | 32 ++++ Zune.Net.Commerce/Program.cs | 62 +++++--- Zune.Net.Commerce/Startup.cs | 67 --------- Zune.Net.Commerce/Zune.Net.Commerce.csproj | 1 + .../appsettings.Development.json | 4 +- Zune.Net.Commerce/appsettings.json | 41 +++++- .../Controllers/MessagingInboxController.cs | 12 +- Zune.Net.Inbox/appsettings.Development.json | 4 +- Zune.Net.Inbox/appsettings.json | 2 +- Zune.Net.Login/appsettings.Development.json | 3 +- Zune.Net.Login/appsettings.json | 2 +- .../appsettings.Development.json | 3 +- Zune.Net.Mix/appsettings.Development.json | 3 +- .../Helpers/HttpResponseMessageResult.cs | 74 ++++++++++ Zune.Net.Shared/Helpers/MusicBrainz.Album.cs | 12 +- Zune.Net.Shared/Middleware/WlidMiddleware.cs | 36 ++++- .../Controllers/MembersController.cs | 9 +- .../appsettings.Development.json | 4 +- Zune.Net.SocialApi/appsettings.json | 2 +- Zune.Net.Tiles/appsettings.Development.json | 3 +- Zune.Net.Tuners/appsettings.Development.json | 3 +- Zune.Xml/Catalog/Track.cs | 8 +- .../GetTunerRegistrationInfoResponse.cs | 51 +++++++ Zune.Xml/Commerce/SignInRequest.cs | 9 +- docker-compose.yml | 1 + nginx/Dockerfile | 2 +- nginx/generate-certs.sh | 2 + nginx/nginx.conf | 26 +++- 44 files changed, 625 insertions(+), 345 deletions(-) delete mode 100644 Zune.Net.Commerce/Config.cs create mode 100644 Zune.Net.Commerce/Controllers/TunerController.cs create mode 100644 Zune.Net.Commerce/Helpers/RequestHelpers.cs delete mode 100644 Zune.Net.Commerce/Startup.cs create mode 100644 Zune.Net.Shared/Helpers/HttpResponseMessageResult.cs create mode 100644 Zune.Xml/Commerce/GetTunerRegistrationInfoResponse.cs create mode 100644 nginx/generate-certs.sh diff --git a/Zune.DB.Console/Program.cs b/Zune.DB.Console/Program.cs index 35874b2..3a3d691 100644 --- a/Zune.DB.Console/Program.cs +++ b/Zune.DB.Console/Program.cs @@ -44,22 +44,27 @@ static async Task Main(string[] args) }); await ctx.ClearMembersAsync(); - await ctx.ClearTokensAsync(); - await ctx.ClearImagesAsync(); + // await ctx.ClearTokensAsync(); + // await ctx.ClearImagesAsync(); + + string userName = "EmailAddress@live.com"; + string SID = "S-1-5-21-414912484-GET YOUR OWN PUNK"; + + // var user = await ctx.GetMemberByName(userName); - string userName = "yoshiask@escargot.chat"; var newMember = new Member { Updated = DateTime.UtcNow, Id = Member.GetGuidFromUserName(userName), + SID = SID, // This is my LiveID SID UserName = userName, PlayCount = 206, Xuid = Member.GetXuidFromUserName(userName), - ZuneTag = "YoshiAsk", - DisplayName = "Yoshi Askharoun", + ZuneTag = "xerootg", + DisplayName = "xerootg", Status = "Reviving the Zune social", - Bio = "A computer science student at Texas A&M Univserity that can't help but bring back dead Microsoft products.", - Location = "College Station, Texas", + Bio = "resident hacker", + Location = "the internet", UserTile = "http://tiles.zune.net/tiles/avatar/default.jpg", Background = "http://tiles.zune.net/tiles/background/USERBACKGROUND-ART-536X196-49.jpg", @@ -79,92 +84,58 @@ static async Task Main(string[] args) BillingInstanceId = "6cba2616-c59a-4dd5-bc9e-d41a45215cfa" }; - var tuner = new Tuner - { - Id = "6cba2616-c59a-4dd5-bc9e-d41a45215cfb" - }; - newMember.TunerRegisterInfo = tuner; - - Guid msgId = Guid.Parse("7e366cd9-6d16-4ddf-9dfe-963acdef4450"); - Guid linkId = Guid.Parse("fe9ab096-a072-475b-8e24-0aaacf32852d"); - var message = new Message - { - DetailsLink = string.Empty, - Sender = newMember, - Status = "hi", - Wishlist = false, - MediaId = msgId, - Subject = "Microsoft revives long-dead Zune product line", - Received = DateTime.UtcNow, - Type = "message", - Id = msgId.ToString(), - AltLink = new Link - { - Href = "https://rr.noordstar.me/microsoft-revives-long-dead-zune-product--f9628d12", - Id = linkId.ToString() - }, - TextContent = "Tech giant Microsoft announced early Monday morning that a new Zune music player is in the works", - }; + + // Guid msgId = Guid.Parse("7e366cd9-6d16-4ddf-9dfe-963acdef4450"); + // Guid linkId = Guid.Parse("fe9ab096-a072-475b-8e24-0aaacf32852d"); + // var message = new Message + // { + // DetailsLink = string.Empty, + // Sender = user, + // Status = "hi", + // Wishlist = false, + // MediaId = msgId, + // Subject = "Microsoft revives long-dead Zune product line", + // Received = DateTime.UtcNow, + // Type = "message", + // Id = msgId.ToString(), + // AltLink = new Link + // { + // Href = "https://rr.noordstar.me/microsoft-revives-long-dead-zune-product--f9628d12", + // Id = linkId.ToString() + // }, + // TextContent = "Tech giant Microsoft announced early Monday morning that a new Zune music player is in the works", + // }; //newMember.Messages ??= new System.Collections.Generic.List(1); //newMember.Messages.Add(message); - Guid badgedId = Guid.Parse("fe9ab096-a072-475b-8e24-0aaacf32852f"); - var badge = new Badge - { - Description = "Restore the Zune social", - TypeId = Xml.SocialApi.BadgeType.ActiveForumsBadge_Gold, - Title = "Necromancer", - Image = "https://i.imgur.com/dMwIZs8.png", - MediaId = Guid.NewGuid(), - MediaType = "Application", - //Summary = "Where is this shown? No idea, contact YoshiAsk if you see this in the software" - }; + // Guid badgedId = Guid.Parse("fe9ab096-a072-475b-8e24-0aaacf32852f"); + // var badge = new Badge + // { + // Description = "Restore the Zune social", + // TypeId = Xml.SocialApi.BadgeType.ActiveForumsBadge_Gold, + // Title = "Necromancer", + // Image = "https://i.imgur.com/dMwIZs8.png", + // MediaId = Guid.NewGuid(), + // MediaType = "Application", + // // Summary = "Where is this shown? No idea, contact YoshiAsk if you see this in the software" + // }; await ctx.CreateAsync(newMember); - //ctx.Messages.Add(message); - //ctx.Tuners.Add(tuner); - userName = "wamwoowam@escargot.chat"; - var member2 = new Member - { - Updated = DateTime.UtcNow, - Id = Member.GetGuidFromUserName(userName), - UserName = userName, - PlayCount = 4123, - Xuid = Member.GetXuidFromUserName(userName), - ZuneTag = "WamWooWam", - DisplayName = string.Empty, - Status = "Restoring Windows Phone 7", - Bio = "he/they, pan, nerd with a strange obsession for windows phone", - Location = "Ireland", - UserTile = "http://i.imgur.com/06BuEKG.jpg", - Background = "http://i.imgur.com/KeZIsxF.jpg", + // var tuner = new Tuner + // { + // Id = "6cba2616-c59a-4dd5-bc9e-d41a45215cfb" + // }; + // user.TunerRegisterInfo = tuner; + // user.SID = SID; + // // user.Badges.Add(badge); + // await ctx.UpdateAsync(user); - AcceptedTermsOfService = true, - AccountSuspended = false, - BillingUnavailable = true, - SubscriptionLapsed = true, - TagChangeRequired = false, - UsageCollectionAllowed = false, - ExplicitPrivilege = false, - Lightweight = false, - Locale = "en-GB", - ParentallyControlled = false, - PointsBalance = 100.0, - SongCreditBalance = 0.0, - SongCreditRenewalDate = DateTime.Now.AddDays(1).ToString("O"), - BillingInstanceId = "6cba2616-c59a-4dd5-bc9e-d41a5f215cfa" - }; + // System.Console.WriteLine($"Set SID for {user.UserName} to {user.SID}"); - tuner = new Tuner - { - Id = "6cba2616-c59a-4dd5-bc9e-d441f315cfb" - }; - member2.TunerRegisterInfo = tuner; + //ctx.Messages.Add(message); + // ctx.Tuners.Add(tuner); - await ctx.CreateAsync(member2); - //ctx.Tuners.Add(tuner); - //ctx.SaveChanges(); } } } diff --git a/Zune.DB/Helpers.cs b/Zune.DB/Helpers.cs index 430c369..3fb84d8 100644 --- a/Zune.DB/Helpers.cs +++ b/Zune.DB/Helpers.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Security.Cryptography; using System.Text; @@ -6,25 +7,36 @@ namespace Zune.DB { public static class Helpers { - public static Guid GenerateGuid(string content) + + private static byte[] GetSha256(string content) { - // Using MD5 here is fine, as these GUIDs aren't used for - // anything meant to be secure. We could use SHA256, but - // we'd have to crush it down to 16 bytes for the GUID - // anyway. Might as well use MD5 ¯\_(ツ)_/¯ + using var hasher = SHA256.Create(); + var byteArrayResultOfRawData = Encoding.UTF8.GetBytes(content); - // Compute 128-bit (16-byte) hash - byte[] hash = MD5.HashData(Encoding.UTF8.GetBytes(content)); - return new(hash); + return hasher.ComputeHash(byteArrayResultOfRawData); + } + + public static Guid GenerateGuid(string content) + { + var result = GetSha256(content); + var guidBase = new byte[16]; + for(int i = 0;i < 16; i++) + { + guidBase[i] = result[i]; + } + return new(guidBase); } public static string Hash(string str) { - byte[] hash = SHA256.HashData(Encoding.UTF8.GetBytes(str)); + var result = GetSha256(str); + + string[] hashStr = new string[result.Length]; - string[] hashStr = new string[hash.Length]; - for (int i = 0; i < hash.Length; i++) - hashStr[i] = hash[i].ToString("X2"); + for (int i = 0; i < result.Length; i++) + { + hashStr[i] = result[i].ToString("X2"); + } return string.Join(string.Empty, hashStr).ToUpperInvariant(); } diff --git a/Zune.DB/Models/Member.cs b/Zune.DB/Models/Member.cs index 8c17626..68c3f4d 100644 --- a/Zune.DB/Models/Member.cs +++ b/Zune.DB/Models/Member.cs @@ -42,6 +42,9 @@ public Guid Id public string UserName { get; set; } + [BsonDefaultValue("NotSet")] + public string SID { get; set; } + public IList Playlists { get; set; } public IList Badges { get; set; } public IList Comments { get; set; } @@ -81,6 +84,7 @@ public Guid Id public string LastLabelTakedownDate { get; set; } public Tuner MediaTypeTunerRegisterInfo { get; set; } + // this is supposed to be a list. public Tuner TunerRegisterInfo { get; set; } public string UserTile { get; set; } diff --git a/Zune.DB/Models/Tuner.cs b/Zune.DB/Models/Tuner.cs index 824a788..0d6f2b4 100644 --- a/Zune.DB/Models/Tuner.cs +++ b/Zune.DB/Models/Tuner.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; namespace Zune.DB.Models { diff --git a/Zune.DB/ZuneNetContext.cs b/Zune.DB/ZuneNetContext.cs index 45e302a..680dee0 100644 --- a/Zune.DB/ZuneNetContext.cs +++ b/Zune.DB/ZuneNetContext.cs @@ -49,6 +49,9 @@ public async Task> GetAsync(Expression> filter = public async Task CreateAsync(Member newMember) => await _memberCollection.InsertOneAsync(newMember); + public async Task UpdateAsync(Member updatedMember) => + await _memberCollection.ReplaceOneAsync(x => x.Id == updatedMember.Id, updatedMember); + public async Task UpdateAsync(Guid id, Member updatedMember) => await _memberCollection.ReplaceOneAsync(x => x.Id == id, updatedMember); @@ -60,9 +63,20 @@ public async Task RemoveAsync(Guid id) => public async Task GetCidByToken(string token) { string tokenHash = Helpers.Hash(token); + Console.WriteLine($"hashedToken = {tokenHash}"); return await _authCollection.Find(e => e.TokenHash == tokenHash).FirstOrDefaultAsync(); } + public async Task GetMemberByName(string UserName) + { + return await GetSingleAsync(m => m.UserName == UserName); + } + + public async Task GetMemberBySid(string sid) + { + return await GetSingleAsync(user => user.SID == sid); + } + public async Task GetMemberByToken(string token) { var entry = await GetCidByToken(token); diff --git a/Zune.Net.Catalog.Image/Controllers/ImageController.cs b/Zune.Net.Catalog.Image/Controllers/ImageController.cs index 9873800..6152808 100644 --- a/Zune.Net.Catalog.Image/Controllers/ImageController.cs +++ b/Zune.Net.Catalog.Image/Controllers/ImageController.cs @@ -1,10 +1,10 @@ using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Globalization; using System.Linq; +using System.Net.Http; using System.Threading.Tasks; using Zune.DB; using Zune.Net.Helpers; @@ -15,13 +15,16 @@ namespace Zune.Net.Catalog.Image.Controllers [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ImageController : Controller { + private static readonly Uri defaultImage = new Uri("https://hellofromseattle.com/wp-content/uploads/sites/6/2020/03/Zune-Basic.png"); private static readonly ConcurrentDictionary dcArtistCache = new(); private static readonly int[] caaSupportedSizes = new[] { 250, 500, 1200 }; private readonly ZuneNetContext _database; - public ImageController(ZuneNetContext database) + private readonly ILogger _logger; + public ImageController(ZuneNetContext database, ILogger logger) { _database = database; + _logger = logger; } [HttpGet, Route("image/{id}")] @@ -67,12 +70,34 @@ public async Task Image(Guid id) imageUrl = $"https://coverartarchive.org/release/{id}/front-{width}"; } + if(Request.Query.TryGetValue("resize", out var resize)) + { + if(bool.TryParse(resize, out var _resize)) + { + if(_resize) // this flag basically states the zune is expecting the image to be the payload, so we hackily respond with that + { + _logger.LogInformation("streaming the image to the zune now!"); + // Probably should pull the contenttype value out of the query... + using var client = new HttpClient(); + var result = await client.GetAsync(imageUrl); + if(!result.IsSuccessStatusCode) + { + _logger.LogInformation("Falling back to the default image, failed to resolve actual artwork"); + result = await client.GetAsync(defaultImage); + } + return new HttpResponseMessageResult(result); + } + } + } + // Request the image from the API and forward it to the Zune software return imageUrl != null ? Redirect(imageUrl) : NotFound(); } + // i.e. http://image.catalog.zune.net/v3.0/en-US/music/track/f32bb0ab-59d6-4620-b239-e86dc68647a4/albumImage?width=240&height=240&resize=true + [HttpGet, Route("music/{imageKind}/{id}/{type}")] public async Task ArtistImage(Guid id, string type) { @@ -80,17 +105,50 @@ public async Task ArtistImage(Guid id, string type) if (type == "primaryImage") { - (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(id); + try + { + _logger.LogInformation($"Getting image for Artist"); + (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(id); + + imageUrl = new Uri(dc_artist.Value("images")? + .FirstOrDefault(i => i.Value("type") == "primary")? + .Value("uri")); + } catch + { + _logger.LogInformation($"Failed to get by Artist ID, failing over to albumImage lookup method"); + type = "albumImage"; + } - imageUrl = new Uri(dc_artist.Value("images")? - .FirstOrDefault(i => i.Value("type") == "primary")? - .Value("uri")); } if(type == "albumImage") { - imageUrl = MusicBrainz.GetAlbumArtByMBID(id); - // var imageId = album.Images.ToArray().First().Id; + try + { + _logger.LogInformation($"Getting image from RecordingID"); + var albumId = MusicBrainz.GetAlbumByRecordingId(id).Id; + _logger.LogInformation($"Got ID{albumId}"); + //e.g.: http://coverartarchive.org/release/93c488f3-1739-455d-a609-85b846b45344/front-250 + imageUrl = new Uri($"http://coverartarchive.org/release/{albumId}/front-250"); + } catch (Exception e) + { + _logger.LogError(e, "Failed to get AlbumID"); + } + if(imageUrl == null) + { + _logger.LogInformation($"Getting image failed, falling back"); + imageUrl = new Uri($"http://coverartarchive.org/release/{id}/front-250"); + } + + _logger.LogInformation($"Getting image from: {imageUrl.AbsoluteUri}"); + + using var client = new HttpClient(); + var result = await client.GetAsync(imageUrl); + if(!result.IsSuccessStatusCode) + { + result = await client.GetAsync(defaultImage); + } + return new HttpResponseMessageResult(result); } return imageUrl != null diff --git a/Zune.Net.Catalog.Image/Program.cs b/Zune.Net.Catalog.Image/Program.cs index f296455..d20d4a7 100644 --- a/Zune.Net.Catalog.Image/Program.cs +++ b/Zune.Net.Catalog.Image/Program.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using System.Threading.Tasks; namespace Zune.Net.Catalog.Image @@ -11,6 +13,11 @@ public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); + builder.Host.ConfigureLogging(cfg => + { + cfg.AddConsole(); + }); + // Add services to the container. builder.Services.AddControllers(); diff --git a/Zune.Net.Catalog.Image/appsettings.Development.json b/Zune.Net.Catalog.Image/appsettings.Development.json index 0c208ae..dc23838 100644 --- a/Zune.Net.Catalog.Image/appsettings.Development.json +++ b/Zune.Net.Catalog.Image/appsettings.Development.json @@ -1,8 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Default": "Debug" } } } diff --git a/Zune.Net.Catalog.Image/appsettings.json b/Zune.Net.Catalog.Image/appsettings.json index 14586bc..cb9dfc0 100644 --- a/Zune.Net.Catalog.Image/appsettings.json +++ b/Zune.Net.Catalog.Image/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongodb:27017", + "ConnectionString": "mongodb://root:rootpassword@mongodb:27017", "DatabaseName": "Zune" }, "Logging": { diff --git a/Zune.Net.Catalog/appsettings.Development.json b/Zune.Net.Catalog/appsettings.Development.json index 8983e0f..dc23838 100644 --- a/Zune.Net.Catalog/appsettings.Development.json +++ b/Zune.Net.Catalog/appsettings.Development.json @@ -1,9 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Default": "Debug" } } } diff --git a/Zune.Net.Catalog/appsettings.json b/Zune.Net.Catalog/appsettings.json index 5c3a13e..a7a8965 100644 --- a/Zune.Net.Catalog/appsettings.json +++ b/Zune.Net.Catalog/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongodb:27017", + "ConnectionString": "mongodb://root:rootpassword@mongodb:27017", "DatabaseName": "Zune" }, "Logging": { diff --git a/Zune.Net.Commerce/Config.cs b/Zune.Net.Commerce/Config.cs deleted file mode 100644 index 4176474..0000000 --- a/Zune.Net.Commerce/Config.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Reflection; - -namespace CommerceZuneNet -{ - public class Config - { - public string Host { get; set; } - public string Port { get; set; } = "80"; - public string SslPort { get; set; } = "443"; - - public Config() { } - - public Config(string[] args) - { - Type cfgType = typeof(Config); - foreach (string arg in args) - { - string key; - object value; - - int idx = arg.IndexOf('='); - if (idx >= 0) - { - key = arg[..idx]; - value = arg[(idx + 1)..]; - } - else - { - key = arg; - value = bool.TrueString; - } - - PropertyInfo prop = cfgType.GetProperty(key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); - if (prop != null) - { - prop.SetValue(this, value); - } - else - { - Console.WriteLine($"Property \"{key}\" does not exist on {nameof(Config)}."); - } - } - } - } -} diff --git a/Zune.Net.Commerce/Controllers/AccountController.cs b/Zune.Net.Commerce/Controllers/AccountController.cs index 74b6d03..ec0ab60 100644 --- a/Zune.Net.Commerce/Controllers/AccountController.cs +++ b/Zune.Net.Commerce/Controllers/AccountController.cs @@ -1,5 +1,10 @@ -using Microsoft.AspNetCore.Mvc; +using System; +using System.IO; using System.Threading.Tasks; +using System.Xml.Serialization; +using CommerceZuneNet.Helpers; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Zune.DB; using Zune.Net.Middleware; using Zune.Xml.Commerce; @@ -12,37 +17,105 @@ namespace CommerceZuneNet.Controllers public class AccountController : ControllerBase { private readonly ZuneNetContext _database; - public AccountController(ZuneNetContext database) + private readonly ILogger _logger; + public AccountController(ZuneNetContext database, ILogger logger) { + _logger = logger; _database = database; } [HttpPost] - public async Task> SignIn(SignInRequest request) + [Produces("application/xml")] + [Consumes("application/x-www-form-urlencoded","application/xml")] + public async Task> SignIn() { - SignInResponse response; if (this.TryGetAuthedMember(out var member)) { - response = member.GetSignInResponse(); + _logger.LogInformation($"Session has been associated with: {member.UserName}"); + return member.GetSignInResponse(); } else { - // TODO: Work out the error response format - response = new SignInResponse + // Get the user by SID and tie the session to that user: + try + { + if(this.TryGetSessionId(out var sessionID)) + { + _logger.LogInformation("We have a session ID, attempting to tie to a SID/User"); + + // parse the SID out of the body the hard way, since the asp.net core xml deserializer chokes on TunerInfo. + var body = await Request.GetRawBodyAsync(); + _logger.LogDebug($"Got a body of: {body.ToString()}"); + using var reader = new StringReader(body); + var serializer = new XmlSerializer(typeof(SignInRequest)); + SignInRequest requestBody = null; + try + { + requestBody = (SignInRequest)serializer.Deserialize(reader); + } + catch (Exception e) + { + _logger.LogError(e,"Failed to deserialize request"); + return Reject(); + } + + if(requestBody != null) + { + // we have a sid, probably + var user_sid = requestBody.TunerInfo.ID; + if(string.IsNullOrEmpty(user_sid)) + { + _logger.LogDebug("failed to get a SID from the request body"); + return Reject(); + } + + _logger.LogDebug($"User SID: {user_sid}"); + + member = await _database.GetMemberBySid(user_sid); + if(member == null) + { + _logger.LogError($"Failed to find a member with SID: {user_sid}"); + return Reject(); + } + _logger.LogInformation("We got a user by SID"); + await _database.AddToken(sessionID, member.UserName); + _logger.LogInformation("Session is associated with SID"); + await _database.UpdateAsync(member); + _logger.LogInformation("Updating the database!"); + member = await _database.GetMemberBySid(user_sid); + return member.GetSignInResponse(); + // attempt to get the user by SID and tie the session back to it. + } else + { + _logger.LogInformation("No sid was recovered, rejecting the request"); + return Reject(); + } + } + } + catch (Exception e) + { + _logger.LogError(e, "Failed to get our user"); + } + } + + return Reject(); + } + + private ActionResult Reject() + { + return Unauthorized(new SignInResponse { AccountState = new AccountState { SignInErrorCode = 0x80070057, } - }; - return Unauthorized(response); - } - - return response; + }); } - [HttpPost] - public ActionResult User(GetUserRequest request) + [HttpPost("User")] + [Produces("application/xml")] + [Consumes("application/x-www-form-urlencoded")] + public ActionResult ZuneUser() { return new(new GetUserResponse()); } diff --git a/Zune.Net.Commerce/Controllers/BillingController.cs b/Zune.Net.Commerce/Controllers/BillingController.cs index d44cf19..86c3542 100644 --- a/Zune.Net.Commerce/Controllers/BillingController.cs +++ b/Zune.Net.Commerce/Controllers/BillingController.cs @@ -1,8 +1,6 @@ using Atom.Xml; using Microsoft.AspNetCore.Mvc; using System; -using System.Security.Cryptography; -using System.Text; using Zune.DB; using Zune.Xml.Commerce; @@ -20,6 +18,7 @@ public BillingController(ZuneNetContext database) } [HttpPost] + [Produces("application/xml")] public ActionResult PurchaseHistory() { return new Feed @@ -41,6 +40,7 @@ public ActionResult PurchaseHistory() } [HttpPost] + [Produces("application/xml")] public ActionResult EnumeratePointsBundles() { var id = Guid.NewGuid().ToString(); diff --git a/Zune.Net.Commerce/Controllers/TunerController.cs b/Zune.Net.Commerce/Controllers/TunerController.cs new file mode 100644 index 0000000..0135f24 --- /dev/null +++ b/Zune.Net.Commerce/Controllers/TunerController.cs @@ -0,0 +1,47 @@ + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Zune.DB; +using Zune.Net.Middleware; +using Zune.Xml.Commerce; + +namespace CommerceZuneNet.Controllers +{ + [Route("/{version}/{language}/tuner")] + [Route("/{language}/tuner")] + public class TunerController : ControllerBase + { + private readonly ILogger _logger; + private readonly ZuneNetContext _database; + public TunerController(ILogger logger, ZuneNetContext database) + { + _logger = logger; + _database = database; + } + + [HttpPost("GetRegistrationInfo")] + // [Produces("application/xml")] + [Consumes("application/x-www-form-urlencoded","application/xml")] + [Produces("application/xml")] + public ActionResult GetRegistrationInfo() + { + if (this.TryGetAuthedMember(out var member)) + { + _logger.LogInformation($"Session has been associated with: {member.UserName}"); + //var unused = new GetTunerRegistrationInfoResponse(); + return Ok(@" + + + + + + + "); + } + + _logger.LogError("Failed to get a user for this session."); + return Unauthorized(); + } + + } +} \ No newline at end of file diff --git a/Zune.Net.Commerce/Dockerfile b/Zune.Net.Commerce/Dockerfile index ef9a908..ee77537 100644 --- a/Zune.Net.Commerce/Dockerfile +++ b/Zune.Net.Commerce/Dockerfile @@ -7,7 +7,7 @@ COPY ./ . RUN dotnet restore RUN dotnet build --no-restore -WORKDIR /source/Zune.Net.Catalog +WORKDIR /source/Zune.Net.Commerce RUN dotnet publish -c release -o /app --no-restore # final stage/image @@ -15,4 +15,4 @@ FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app COPY --from=build /app ./ ENV DOTNET_EnableDiagnostics=0 -ENTRYPOINT ["dotnet", "Zune.Net.Catalog.dll"] +ENTRYPOINT ["dotnet", "Zune.Net.Commerce.dll"] diff --git a/Zune.Net.Commerce/Helpers/RequestHelpers.cs b/Zune.Net.Commerce/Helpers/RequestHelpers.cs new file mode 100644 index 0000000..8d5e920 --- /dev/null +++ b/Zune.Net.Commerce/Helpers/RequestHelpers.cs @@ -0,0 +1,32 @@ +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +// Credit: https://markb.uk/asp-net-core-read-raw-request-body-as-string.html +namespace CommerceZuneNet.Helpers; +public static class RequestBodyHelpers +{ + public static async Task GetRawBodyAsync( + this HttpRequest request, + Encoding encoding = null) +{ + if (!request.Body.CanSeek) + { + // We only do this if the stream isn't *already* seekable, + // as EnableBuffering will create a new stream instance + // each time it's called + request.EnableBuffering(); + } + + request.Body.Position = 0; + + var reader = new StreamReader(request.Body, encoding ?? Encoding.UTF8); + + var body = await reader.ReadToEndAsync().ConfigureAwait(false); + + request.Body.Position = 0; + + return body; +} +} \ No newline at end of file diff --git a/Zune.Net.Commerce/Program.cs b/Zune.Net.Commerce/Program.cs index dbc825d..d9c0edd 100644 --- a/Zune.Net.Commerce/Program.cs +++ b/Zune.Net.Commerce/Program.cs @@ -1,8 +1,14 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.AspNetCore.Mvc.Formatters.Xml; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Zune.Net; +using Zune.Net.Middleware; namespace CommerceZuneNet { @@ -10,32 +16,40 @@ public class Program { public static void Main(string[] args) { - CreateHostBuilder(args).Build().Run(); - } + var builder = WebApplication.CreateBuilder(args); + builder.Host.ConfigureLogging(cfg => + { + cfg.AddConsole(); + }); - public static IHostBuilder CreateHostBuilder(string[] args) - { - Config cfg = new(args); + // Add services to the container. + builder.Services.AddControllers().AddXmlSerializerFormatters(); + builder.Services.AddReverseProxy() + .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); - return Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - if (cfg.Host != null) - webBuilder.UseUrls($"http://{cfg.Host}:{cfg.Port}", $"https://{cfg.Host}:{cfg.SslPort}"); - }) - .ConfigureAppConfiguration((hostingContext, config) => - { - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - config.AddEnvironmentVariables(); - }) - .ConfigureServices((ctx, s) => + builder.Host.ConfigureZuneDB(); + + var app = builder.Build(); + + // Configure the HTTP request pipeline. + app.UseRequestBuffering(); + + app.UseRouting(); + + app.UseWlidAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + + endpoints.MapGet("/", ctx => { - s.AddSingleton(sp => cfg); - }) - .ConfigureZuneDB(); + return Task.FromResult(new OkObjectResult("Welcome to the Social")); + }); + endpoints.MapReverseProxy(); + }); + + app.Run(); } } } diff --git a/Zune.Net.Commerce/Startup.cs b/Zune.Net.Commerce/Startup.cs deleted file mode 100644 index ff50fc3..0000000 --- a/Zune.Net.Commerce/Startup.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Models; -using Zune.Net; -using Zune.Net.Middleware; - -namespace CommerceZuneNet -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllersWithViews(o => o.UseZestFormatters()); - services.AddSwaggerGen(c => - { - c.SwaggerDoc("CommerceZuneNet", new OpenApiInfo { Title = "CommerceZuneNet", Version = "v2" }); - }); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, Config cfg) - { - LoggerFactory.Create(loggerFactory => - { - loggerFactory.AddConsole(); - loggerFactory.AddDebug(); - }); - - app.UseStatusCodePages(); - - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseSwagger(); - app.UseSwaggerUI(c => { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "Commerce.Zune.Net v2"); - c.RoutePrefix = string.Empty; - //c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); - }); - } - - app.UseRequestBuffering(); - - // app.UseHttpsRedirection(); - - app.UseRouting(); - app.UseWlidAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } - } -} diff --git a/Zune.Net.Commerce/Zune.Net.Commerce.csproj b/Zune.Net.Commerce/Zune.Net.Commerce.csproj index aa4614c..7b643ae 100644 --- a/Zune.Net.Commerce/Zune.Net.Commerce.csproj +++ b/Zune.Net.Commerce/Zune.Net.Commerce.csproj @@ -11,6 +11,7 @@ + diff --git a/Zune.Net.Commerce/appsettings.Development.json b/Zune.Net.Commerce/appsettings.Development.json index 63ed6e1..dc23838 100644 --- a/Zune.Net.Commerce/appsettings.Development.json +++ b/Zune.Net.Commerce/appsettings.Development.json @@ -1,9 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft": "Information", - "Microsoft.Hosting.Lifetime": "Information" + "Default": "Debug" } } } diff --git a/Zune.Net.Commerce/appsettings.json b/Zune.Net.Commerce/appsettings.json index 9ad180b..d6ead0c 100644 --- a/Zune.Net.Commerce/appsettings.json +++ b/Zune.Net.Commerce/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongodb:27017", + "ConnectionString": "mongodb://root:rootpassword@mongodb:27017", "DatabaseName": "Zune", "MemberCollectionName": "Members" }, @@ -19,9 +19,42 @@ }, "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Default": "Debug" + } + }, + "ReverseProxy": + { + "Routes": + { + "localeinbox": + { + "ClusterId": "inbox", + "Match": + { + "Path": "{locale}/messaging/{zuneTag}/inbox/{**catchall}" + } + }, + "inbox": + { + "ClusterId": "inbox", + "Match": + { + "Path": "/messaging/{zuneTag}/inbox/{**catchall}" + } + } + }, + "Clusters": + { + "inbox": + { + "Destinations": + { + "inbox.zune.net": + { + "Address": "http://inbox.zune.net" + } + } + } } }, "AllowedHosts": "*" diff --git a/Zune.Net.Inbox/Controllers/MessagingInboxController.cs b/Zune.Net.Inbox/Controllers/MessagingInboxController.cs index 02ebee1..9847e58 100644 --- a/Zune.Net.Inbox/Controllers/MessagingInboxController.cs +++ b/Zune.Net.Inbox/Controllers/MessagingInboxController.cs @@ -1,15 +1,6 @@ using Atom.Xml; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using System.Xml.Serialization; -using Zune.DB; -using Zune.DB.Models; using Zune.Xml.Inbox; namespace Zune.Net.Inbox.Controllers @@ -68,13 +59,14 @@ public ActionResult Details(string locale, string zuneTag, strin [HttpGet] public IActionResult UnreadCont(string locale, string zuneTag) { + // using var ctx = new ZuneNetContext(); // Member member = ctx.Members.First(m => m.ZuneTag == zuneTag); // if (member == null) // return StatusCode(StatusCodes.Status400BadRequest, $"User {zuneTag} does not exist."); // return Content(member.Messages.Count(msg => !msg.IsRead).ToString()); - return Content("1"); + return Ok("1"); } } } diff --git a/Zune.Net.Inbox/appsettings.Development.json b/Zune.Net.Inbox/appsettings.Development.json index 8983e0f..dc23838 100644 --- a/Zune.Net.Inbox/appsettings.Development.json +++ b/Zune.Net.Inbox/appsettings.Development.json @@ -1,9 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Default": "Debug" } } } diff --git a/Zune.Net.Inbox/appsettings.json b/Zune.Net.Inbox/appsettings.json index cffac35..8383a8a 100644 --- a/Zune.Net.Inbox/appsettings.json +++ b/Zune.Net.Inbox/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongodb:27017", + "ConnectionString": "mongodb://root:rootpassword@mongodb:27017", "DatabaseName": "Zune", "MemberCollectionName": "Members" }, diff --git a/Zune.Net.Login/appsettings.Development.json b/Zune.Net.Login/appsettings.Development.json index 0c208ae..dc23838 100644 --- a/Zune.Net.Login/appsettings.Development.json +++ b/Zune.Net.Login/appsettings.Development.json @@ -1,8 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Default": "Debug" } } } diff --git a/Zune.Net.Login/appsettings.json b/Zune.Net.Login/appsettings.json index ac2b7d4..78df620 100644 --- a/Zune.Net.Login/appsettings.json +++ b/Zune.Net.Login/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongodb:27017", + "ConnectionString": "mongodb://root:rootpassword@mongodb:27017", "DatabaseName": "Zune", }, "Logging": { diff --git a/Zune.Net.MetaServices/appsettings.Development.json b/Zune.Net.MetaServices/appsettings.Development.json index 0c208ae..dc23838 100644 --- a/Zune.Net.MetaServices/appsettings.Development.json +++ b/Zune.Net.MetaServices/appsettings.Development.json @@ -1,8 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Default": "Debug" } } } diff --git a/Zune.Net.Mix/appsettings.Development.json b/Zune.Net.Mix/appsettings.Development.json index 0c208ae..dc23838 100644 --- a/Zune.Net.Mix/appsettings.Development.json +++ b/Zune.Net.Mix/appsettings.Development.json @@ -1,8 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Default": "Debug" } } } diff --git a/Zune.Net.Shared/Helpers/HttpResponseMessageResult.cs b/Zune.Net.Shared/Helpers/HttpResponseMessageResult.cs new file mode 100644 index 0000000..c4543ee --- /dev/null +++ b/Zune.Net.Shared/Helpers/HttpResponseMessageResult.cs @@ -0,0 +1,74 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Mvc; + +// Credit: https://stackoverflow.com/questions/29975001/how-to-read-httpresponsemessage-content-as-text +public class HttpResponseMessageResult : IActionResult +{ + private readonly HttpResponseMessage _responseMessage; + + public HttpResponseMessageResult(HttpResponseMessage responseMessage) + { + _responseMessage = responseMessage; // could add throw if null + } + + public async Task ExecuteResultAsync(ActionContext context) + { + var response = context.HttpContext.Response; + + + if (_responseMessage == null) + { + var message = "Response message cannot be null"; + + throw new InvalidOperationException(message); + } + + using (_responseMessage) + { + response.StatusCode = (int)_responseMessage.StatusCode; + + var responseFeature = context.HttpContext.Features.Get(); + if (responseFeature != null) + { + responseFeature.ReasonPhrase = _responseMessage.ReasonPhrase; + } + + var responseHeaders = _responseMessage.Headers; + + // Ignore the Transfer-Encoding header if it is just "chunked". + // We let the host decide about whether the response should be chunked or not. + if (responseHeaders.TransferEncodingChunked == true && + responseHeaders.TransferEncoding.Count == 1) + { + responseHeaders.TransferEncoding.Clear(); + } + + foreach (var header in responseHeaders) + { + response.Headers.Append(header.Key, header.Value.ToArray()); + } + + if (_responseMessage.Content != null) + { + var contentHeaders = _responseMessage.Content.Headers; + + // Copy the response content headers only after ensuring they are complete. + // We ask for Content-Length first because HttpContent lazily computes this + // and only afterwards writes the value into the content headers. + var unused = contentHeaders.ContentLength; + + foreach (var header in contentHeaders) + { + response.Headers.Append(header.Key, header.Value.ToArray()); + } + + await _responseMessage.Content.CopyToAsync(response.Body); + } + } + } +} \ No newline at end of file diff --git a/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs b/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs index 1b8fe7a..7dc0bae 100644 --- a/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs +++ b/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs @@ -34,16 +34,12 @@ public static Album GetAlbumByMBID(Guid mbid) return MBReleaseToAlbum(mb_rel); } - public static Uri GetAlbumArtByMBID(Guid mbid) + public static Album GetAlbumByRecordingId(Guid mbid) { - var mb_rel = _query.LookupRelease(mbid, Include.Genres | Include.ArtistCredits | Include.Recordings | Include.Media); - if(mb_rel.CoverArtArchive.Front) - { - new Uri($"http://coverartarchive.org/release/{mbid}/front"); - } - return null; - } + var mb_rel = _query.LookupRecording(mbid, Include.Releases | Include.ArtistCredits); + return MBReleaseToAlbum(mb_rel.Releases.First()); + } public static Album MBReleaseToAlbum(IRelease mb_rel, DateTime? updated = null, bool includeRights = true) { diff --git a/Zune.Net.Shared/Middleware/WlidMiddleware.cs b/Zune.Net.Shared/Middleware/WlidMiddleware.cs index d89faa5..0a77da4 100644 --- a/Zune.Net.Shared/Middleware/WlidMiddleware.cs +++ b/Zune.Net.Shared/Middleware/WlidMiddleware.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using System; using System.Threading.Tasks; using Zune.DB; using Zune.DB.Models; @@ -10,6 +11,7 @@ namespace Zune.Net.Middleware public class WlidMiddleware { internal const string AUTHED_MEMBER_KEY = "Member"; + internal const string WLID_SESSION_ID = "WLID-SESSIONID"; private readonly RequestDelegate _next; public WlidMiddleware(RequestDelegate next) @@ -20,23 +22,36 @@ public WlidMiddleware(RequestDelegate next) public async Task InvokeAsync(HttpContext context, ZuneNetContext database) { Member authedMember = null; + var authHeader = context.Request.Headers.Authorization; if (authHeader.Count > 0) { - string token = authHeader[0]; - int idxToken = token.IndexOf(' '); - if (idxToken >= 0) - token = token[(idxToken + 1)..]; + var token = authHeader[0]; - if (!string.IsNullOrWhiteSpace(token) && database != null) + // This is how the Escargot project grabs the session, presumably it's reliable enough? + // WLID1.0 t=[0-20] - session ID + if(!string.IsNullOrWhiteSpace(token) && token.StartsWith("WLID1.0 t=") && database != null) { + var idxToken = token.IndexOf(' '); + if (idxToken >= 0) + { + var start = idxToken + 3; + var stop = idxToken + 23; + token = token[start..stop]; + context.Items.Add(WLID_SESSION_ID, token); + } + authedMember = await database.GetMemberByToken(token); } - } - + } + if (authedMember != null) + { context.Items.Add(AUTHED_MEMBER_KEY, authedMember); + // holy cow I wish I had an ILogger here. + Console.WriteLine($"Authed Member: {authedMember.ZuneTag}"); + } // Call the next delegate/middleware in the pipeline. await _next(context); @@ -56,5 +71,12 @@ public static bool TryGetAuthedMember(this ControllerBase controller, out Member member = obj as Member; return isSuccess; } + + public static bool TryGetSessionId(this ControllerBase controller, out string wlid_session_id) + { + bool isSuccess = controller.HttpContext.Items.TryGetValue(WlidMiddleware.WLID_SESSION_ID, out var obj); + wlid_session_id = obj as string; + return isSuccess; + } } } diff --git a/Zune.Net.SocialApi/Controllers/MembersController.cs b/Zune.Net.SocialApi/Controllers/MembersController.cs index eb651ae..2e96e2a 100644 --- a/Zune.Net.SocialApi/Controllers/MembersController.cs +++ b/Zune.Net.SocialApi/Controllers/MembersController.cs @@ -28,17 +28,12 @@ public async Task> Info(string zuneTag) { var member = await _database.GetByIdOrZuneTag(zuneTag); - Member response; if (member != null) { - response = member.GetXmlMember(); - } - else - { - return NotFound(); + return member.GetXmlMember(); } - return response; + return NotFound(); } [Route("{zuneTag}/friends")] diff --git a/Zune.Net.SocialApi/appsettings.Development.json b/Zune.Net.SocialApi/appsettings.Development.json index 8983e0f..dc23838 100644 --- a/Zune.Net.SocialApi/appsettings.Development.json +++ b/Zune.Net.SocialApi/appsettings.Development.json @@ -1,9 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Default": "Debug" } } } diff --git a/Zune.Net.SocialApi/appsettings.json b/Zune.Net.SocialApi/appsettings.json index cffac35..8383a8a 100644 --- a/Zune.Net.SocialApi/appsettings.json +++ b/Zune.Net.SocialApi/appsettings.json @@ -1,6 +1,6 @@ { "ZuneNetContext": { - "ConnectionString": "mongodb://mongodb:27017", + "ConnectionString": "mongodb://root:rootpassword@mongodb:27017", "DatabaseName": "Zune", "MemberCollectionName": "Members" }, diff --git a/Zune.Net.Tiles/appsettings.Development.json b/Zune.Net.Tiles/appsettings.Development.json index 0c208ae..dc23838 100644 --- a/Zune.Net.Tiles/appsettings.Development.json +++ b/Zune.Net.Tiles/appsettings.Development.json @@ -1,8 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Default": "Debug" } } } diff --git a/Zune.Net.Tuners/appsettings.Development.json b/Zune.Net.Tuners/appsettings.Development.json index 0c208ae..dc23838 100644 --- a/Zune.Net.Tuners/appsettings.Development.json +++ b/Zune.Net.Tuners/appsettings.Development.json @@ -1,8 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Default": "Debug" } } } diff --git a/Zune.Xml/Catalog/Track.cs b/Zune.Xml/Catalog/Track.cs index cc9a298..9a74a9a 100644 --- a/Zune.Xml/Catalog/Track.cs +++ b/Zune.Xml/Catalog/Track.cs @@ -39,16 +39,16 @@ public class Track : Media public int PointsPrice { get; set; } [XmlElement("CanPlay")] - public bool CanPlay { get; set; } + public bool CanPlay { get; set; } = true; [XmlElement("CanDownload")] - public bool CanDownload { get; set; } + public bool CanDownload { get; set; } = true; [XmlElement("CanPurchase")] - public bool CanPurchase { get; set; } + public bool CanPurchase { get; set; } = true; [XmlElement("CanPurchaseMP3")] - public bool CanPurchaseMP3 { get; set; } + public bool CanPurchaseMP3 { get; set; } = true; [XmlElement("CanPurchaseAlbumOnly")] public bool CanPurchaseAlbumOnly { get; set; } diff --git a/Zune.Xml/Commerce/GetTunerRegistrationInfoResponse.cs b/Zune.Xml/Commerce/GetTunerRegistrationInfoResponse.cs new file mode 100644 index 0000000..bdbfeaa --- /dev/null +++ b/Zune.Xml/Commerce/GetTunerRegistrationInfoResponse.cs @@ -0,0 +1,51 @@ +using Atom; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Xml.Serialization; + +// this class is not production-ready. The signin workflow needs to actually build this correctly +namespace Zune.Xml.Commerce +{ + public enum MediaTypeEnum + { + Subscription, + AppStore, + } + + public class TunerInfoDef + { + [XmlElement("ID")] + [Required] + public string TunerId { get; set; } + + [XmlElement("Name")] + public string Name { get; set; } + + [XmlElement("RegistrationDate")] + public DateTime RegistrationDate { get; set; } + [XmlElement("Type")] + public string Type { get; set; } + + [XmlElement("Version")] + public string Version { get; set; } + + } + public class MediaTypeTunerPair + { + [XmlArray("RegisteredType")] + public MediaTypeEnum RegisteredType { get; set; } + [XmlArray("TunerList")] + public List TunerList { get; set; } + [XmlArray("NextTunerTypeDeregistrationDate")] + public List> NextTunerTypeDeregistrationDate { get; set; } + [XmlArray("TunerTypeMaxRegistered")] + public List> TunerTypeMaxRegistered { get; set; } + } + [XmlRoot(nameof(GetTunerRegistrationInfoResponse), Namespace = Constants.ZUNE_COMMERCE_NAMESPACE)] + public class GetTunerRegistrationInfoResponse + { + [XmlArray("RegisteredTuners/MediaTypeTunerPair")] + public List MediaTypeTunerPair { get; set; } + } +} \ No newline at end of file diff --git a/Zune.Xml/Commerce/SignInRequest.cs b/Zune.Xml/Commerce/SignInRequest.cs index 72bbd99..7be9c97 100644 --- a/Zune.Xml/Commerce/SignInRequest.cs +++ b/Zune.Xml/Commerce/SignInRequest.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Xml.Serialization; +using System.Xml.Serialization; using Atom; namespace Zune.Xml.Commerce { - [XmlRoot(ElementName = nameof(SignInRequest), Namespace = Constants.ZUNE_COMMERCE_NAMESPACE)] + [XmlRoot(nameof(SignInRequest), Namespace = Constants.ZUNE_COMMERCE_NAMESPACE)] public class SignInRequest { + public SignInRequest(){} + public TunerInfo TunerInfo { get; set; } } } diff --git a/docker-compose.yml b/docker-compose.yml index 27d2e9c..2465c9c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,6 +26,7 @@ services: context: nginx ports: - 80:80 + - 443:443 networks: - backend - zune.net diff --git a/nginx/Dockerfile b/nginx/Dockerfile index 9c54268..f61d9d2 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -1,3 +1,3 @@ FROM nginx:alpine - +COPY ssl/ /etc/ssl/ COPY nginx.conf /etc/nginx/nginx.conf \ No newline at end of file diff --git a/nginx/generate-certs.sh b/nginx/generate-certs.sh new file mode 100644 index 0000000..635e489 --- /dev/null +++ b/nginx/generate-certs.sh @@ -0,0 +1,2 @@ +#! /bin/bash +openssl req -x509 -nodes -days 365 -subj "/C=CA/ST=QC/O=Microsoft, Inc./CN=*.zune.net" -newkey rsa:2048 -keyout ./ssl/private/nginx-selfsigned.key -out ./ssl/certs/nginx-selfsigned.crt; \ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 323f4a0..c6e74ff 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -6,10 +6,6 @@ http { sendfile on; - # upstream catalog { - # server catalog:8080; - # } - server { listen 80; server_name catalog.zune.net; @@ -38,14 +34,15 @@ http { server { listen 80; + listen 443 ssl; server_name commerce.zune.net; + + ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt; + ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key; + location / { proxy_pass http://commerce; proxy_redirect off; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Host $server_name; } } @@ -114,6 +111,19 @@ http { } } + server { + listen 80; + server_name socialapi.zune.net; + location / { + proxy_pass http://social; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } + server { listen 80; server_name login.zune.net; From ac96eea82c763e2076e5e92885bc2f46c31137f7 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Fri, 17 Feb 2023 17:15:04 -0700 Subject: [PATCH 10/29] The RP was not needed, I had an nginx config error, [required] is not availble in all build targets. --- .../Controllers/AccountController.cs | 4 ++- Zune.Net.Commerce/Program.cs | 3 -- Zune.Net.Commerce/Zune.Net.Commerce.csproj | 1 - Zune.Net.Commerce/appsettings.json | 35 ------------------- .../GetTunerRegistrationInfoResponse.cs | 2 -- nginx/nginx.conf | 12 +++++++ 6 files changed, 15 insertions(+), 42 deletions(-) diff --git a/Zune.Net.Commerce/Controllers/AccountController.cs b/Zune.Net.Commerce/Controllers/AccountController.cs index ec0ab60..a996264 100644 --- a/Zune.Net.Commerce/Controllers/AccountController.cs +++ b/Zune.Net.Commerce/Controllers/AccountController.cs @@ -83,8 +83,10 @@ public async Task> SignIn() await _database.UpdateAsync(member); _logger.LogInformation("Updating the database!"); member = await _database.GetMemberBySid(user_sid); + + // TODO: We need to be adding the TunerInfo as a tuner to the db when we see a NEW one. + return member.GetSignInResponse(); - // attempt to get the user by SID and tie the session back to it. } else { _logger.LogInformation("No sid was recovered, rejecting the request"); diff --git a/Zune.Net.Commerce/Program.cs b/Zune.Net.Commerce/Program.cs index d9c0edd..14dcd99 100644 --- a/Zune.Net.Commerce/Program.cs +++ b/Zune.Net.Commerce/Program.cs @@ -24,8 +24,6 @@ public static void Main(string[] args) // Add services to the container. builder.Services.AddControllers().AddXmlSerializerFormatters(); - builder.Services.AddReverseProxy() - .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); builder.Host.ConfigureZuneDB(); @@ -46,7 +44,6 @@ public static void Main(string[] args) { return Task.FromResult(new OkObjectResult("Welcome to the Social")); }); - endpoints.MapReverseProxy(); }); app.Run(); diff --git a/Zune.Net.Commerce/Zune.Net.Commerce.csproj b/Zune.Net.Commerce/Zune.Net.Commerce.csproj index 7b643ae..aa4614c 100644 --- a/Zune.Net.Commerce/Zune.Net.Commerce.csproj +++ b/Zune.Net.Commerce/Zune.Net.Commerce.csproj @@ -11,7 +11,6 @@ - diff --git a/Zune.Net.Commerce/appsettings.json b/Zune.Net.Commerce/appsettings.json index d6ead0c..72801b2 100644 --- a/Zune.Net.Commerce/appsettings.json +++ b/Zune.Net.Commerce/appsettings.json @@ -22,40 +22,5 @@ "Default": "Debug" } }, - "ReverseProxy": - { - "Routes": - { - "localeinbox": - { - "ClusterId": "inbox", - "Match": - { - "Path": "{locale}/messaging/{zuneTag}/inbox/{**catchall}" - } - }, - "inbox": - { - "ClusterId": "inbox", - "Match": - { - "Path": "/messaging/{zuneTag}/inbox/{**catchall}" - } - } - }, - "Clusters": - { - "inbox": - { - "Destinations": - { - "inbox.zune.net": - { - "Address": "http://inbox.zune.net" - } - } - } - } - }, "AllowedHosts": "*" } diff --git a/Zune.Xml/Commerce/GetTunerRegistrationInfoResponse.cs b/Zune.Xml/Commerce/GetTunerRegistrationInfoResponse.cs index bdbfeaa..8bac0d2 100644 --- a/Zune.Xml/Commerce/GetTunerRegistrationInfoResponse.cs +++ b/Zune.Xml/Commerce/GetTunerRegistrationInfoResponse.cs @@ -1,7 +1,6 @@ using Atom; using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using System.Xml.Serialization; // this class is not production-ready. The signin workflow needs to actually build this correctly @@ -16,7 +15,6 @@ public enum MediaTypeEnum public class TunerInfoDef { [XmlElement("ID")] - [Required] public string TunerId { get; set; } [XmlElement("Name")] diff --git a/nginx/nginx.conf b/nginx/nginx.conf index c6e74ff..4093e58 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -48,7 +48,11 @@ http { server { listen 80; + listen 443 ssl; server_name inbox.zune.net; + + ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt; + ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key; location / { proxy_pass http://inbox; proxy_redirect off; @@ -100,7 +104,11 @@ http { server { listen 80; + listen 443 ssl; server_name social.zune.net; + + ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt; + ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key; location / { proxy_pass http://social; proxy_redirect off; @@ -113,7 +121,11 @@ http { server { listen 80; + listen 443 ssl; server_name socialapi.zune.net; + + ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt; + ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key; location / { proxy_pass http://social; proxy_redirect off; From 3a473d55d643a9ac751e21821d0843bb5bbc4147 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Fri, 17 Feb 2023 17:35:35 -0700 Subject: [PATCH 11/29] significant container build time improvements by building/publishing once --- Atom/Atom.csproj | 4 ++++ Zune.DB/Zune.DB.csproj | 4 ++++ Zune.Net.Catalog.Image/Dockerfile | 6 ++---- Zune.Net.Catalog/Dockerfile | 4 +--- Zune.Net.Commerce/Dockerfile | 4 +--- Zune.Net.Inbox/Dockerfile | 4 +--- Zune.Net.Login/Dockerfile | 4 +--- Zune.Net.MetaServices/Dockerfile | 4 +--- Zune.Net.Mix/Dockerfile | 4 +--- Zune.Net.SocialApi/Dockerfile | 4 +--- Zune.Net.Tiles/Dockerfile | 4 +--- Zune.Net.Tuners/Dockerfile | 4 +--- Zune.Xml/Zune.Xml.csproj | 4 ++++ 13 files changed, 23 insertions(+), 31 deletions(-) diff --git a/Atom/Atom.csproj b/Atom/Atom.csproj index 64ca019..f0286c3 100644 --- a/Atom/Atom.csproj +++ b/Atom/Atom.csproj @@ -6,6 +6,10 @@ 0.1.0 + + false + + diff --git a/Zune.DB/Zune.DB.csproj b/Zune.DB/Zune.DB.csproj index 0d5fec7..8aa2900 100644 --- a/Zune.DB/Zune.DB.csproj +++ b/Zune.DB/Zune.DB.csproj @@ -4,6 +4,10 @@ net6.0 + + false + + diff --git a/Zune.Net.Catalog.Image/Dockerfile b/Zune.Net.Catalog.Image/Dockerfile index 5031045..6eb0a00 100644 --- a/Zune.Net.Catalog.Image/Dockerfile +++ b/Zune.Net.Catalog.Image/Dockerfile @@ -5,14 +5,12 @@ WORKDIR /source # cache the build result to speed up subsequent package steps COPY ./ . RUN dotnet restore -RUN dotnet build --no-restore -WORKDIR /source/Zune.Net.Catalog.Image RUN dotnet publish -c release -o /app --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app ./ +COPY --from=build /app/Zune.Net.Catalog.Image ./ ENV DOTNET_EnableDiagnostics=0 -ENTRYPOINT ["dotnet", "Zune.Net.Catalog.Image.dll"] +ENTRYPOINT ["dotnet", "Zune.Net.Catalog.Image.dll"] \ No newline at end of file diff --git a/Zune.Net.Catalog/Dockerfile b/Zune.Net.Catalog/Dockerfile index 15bf685..bb32f44 100644 --- a/Zune.Net.Catalog/Dockerfile +++ b/Zune.Net.Catalog/Dockerfile @@ -5,14 +5,12 @@ WORKDIR /source # cache the build result to speed up subsequent package steps COPY ./ . RUN dotnet restore -RUN dotnet build --no-restore -WORKDIR /source/Zune.Net.Catalog RUN dotnet publish -c release -o /app --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app ./ +COPY --from=build /app/Zune.Net.Catalog ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Catalog.dll"] diff --git a/Zune.Net.Commerce/Dockerfile b/Zune.Net.Commerce/Dockerfile index ee77537..2f13ac8 100644 --- a/Zune.Net.Commerce/Dockerfile +++ b/Zune.Net.Commerce/Dockerfile @@ -5,14 +5,12 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore -RUN dotnet build --no-restore -WORKDIR /source/Zune.Net.Commerce RUN dotnet publish -c release -o /app --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app ./ +COPY --from=build /app/Zune.Net.Commerce ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Commerce.dll"] diff --git a/Zune.Net.Inbox/Dockerfile b/Zune.Net.Inbox/Dockerfile index ae89b00..b791534 100644 --- a/Zune.Net.Inbox/Dockerfile +++ b/Zune.Net.Inbox/Dockerfile @@ -5,14 +5,12 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore -RUN dotnet build --no-restore -WORKDIR /source/Zune.Net.Inbox RUN dotnet publish -c release -o /app --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app ./ +COPY --from=build /app/Zune.Net.Inbox ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Inbox.dll"] diff --git a/Zune.Net.Login/Dockerfile b/Zune.Net.Login/Dockerfile index bae9893..e9cc888 100644 --- a/Zune.Net.Login/Dockerfile +++ b/Zune.Net.Login/Dockerfile @@ -5,14 +5,12 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore -RUN dotnet build --no-restore -WORKDIR /source/Zune.Net.Login RUN dotnet publish -c release -o /app --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app ./ +COPY --from=build /app/Zune.Net.Login ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Login.dll"] diff --git a/Zune.Net.MetaServices/Dockerfile b/Zune.Net.MetaServices/Dockerfile index 2c5c07b..b625ead 100644 --- a/Zune.Net.MetaServices/Dockerfile +++ b/Zune.Net.MetaServices/Dockerfile @@ -5,14 +5,12 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore -RUN dotnet build --no-restore -WORKDIR /source/Zune.Net.MetaServices RUN dotnet publish -c release -o /app --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app ./ +COPY --from=build /app/Zune.Net.MetaServices ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.MetaServices.dll"] diff --git a/Zune.Net.Mix/Dockerfile b/Zune.Net.Mix/Dockerfile index 6315b33..ae50032 100644 --- a/Zune.Net.Mix/Dockerfile +++ b/Zune.Net.Mix/Dockerfile @@ -5,14 +5,12 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore -RUN dotnet build --no-restore -WORKDIR /source/Zune.Net.Mix RUN dotnet publish -c release -o /app --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app ./ +COPY --from=build /app/Zune.Net.Mix ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Mix.dll"] diff --git a/Zune.Net.SocialApi/Dockerfile b/Zune.Net.SocialApi/Dockerfile index e818394..03ae370 100644 --- a/Zune.Net.SocialApi/Dockerfile +++ b/Zune.Net.SocialApi/Dockerfile @@ -5,14 +5,12 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore -RUN dotnet build --no-restore -WORKDIR /source/Zune.Net.SocialApi RUN dotnet publish -c release -o /app --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app ./ +COPY --from=build /app/Zune.Net.SocialApi ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.SocialApi.dll"] diff --git a/Zune.Net.Tiles/Dockerfile b/Zune.Net.Tiles/Dockerfile index 9dda067..e4989de 100644 --- a/Zune.Net.Tiles/Dockerfile +++ b/Zune.Net.Tiles/Dockerfile @@ -5,14 +5,12 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore -RUN dotnet build --no-restore -WORKDIR /source/Zune.Net.Tiles RUN dotnet publish -c release -o /app --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app ./ +COPY --from=build /app/Zune.Net.Tiles ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Tiles.dll"] diff --git a/Zune.Net.Tuners/Dockerfile b/Zune.Net.Tuners/Dockerfile index 96274e4..eb993c6 100644 --- a/Zune.Net.Tuners/Dockerfile +++ b/Zune.Net.Tuners/Dockerfile @@ -5,14 +5,12 @@ WORKDIR /source # copy csproj and restore as distinct layers COPY ./ . RUN dotnet restore -RUN dotnet build --no-restore -WORKDIR /source/Zune.Net.Tuners RUN dotnet publish -c release -o /app --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app ./ +COPY --from=build /app/Zune.Net.Tuners ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Tuners.dll"] diff --git a/Zune.Xml/Zune.Xml.csproj b/Zune.Xml/Zune.Xml.csproj index 74307e8..e9c86a2 100644 --- a/Zune.Xml/Zune.Xml.csproj +++ b/Zune.Xml/Zune.Xml.csproj @@ -6,6 +6,10 @@ 0.1.0 + + false + + $(DefineConstants);NETSTANDARD1_1_OR_GREATER;NETSTANDARD2_0_OR_GREATER;NETSTANDARD2_1_OR_GREATER From e494d33dad8269cede89bee0783b31e82f50dba2 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Fri, 28 Apr 2023 13:30:39 -0600 Subject: [PATCH 12/29] Partially working metaservices --- .vscode/launch.json | 35 ++++++ .vscode/tasks.json | 41 +++++++ Zune.Net.Catalog.Image/Dockerfile | 4 +- .../Controllers/Music/AlbumController.cs | 2 +- Zune.Net.Catalog/Dockerfile | 4 +- Zune.Net.Commerce/Dockerfile | 4 +- .../appsettings.Development.json | 5 +- .../Controllers/MessagingInboxController.cs | 3 +- Zune.Net.Inbox/Dockerfile | 4 +- Zune.Net.Login/Dockerfile | 4 +- Zune.Net.MetaServices/Controllers/Cover.cs | 14 +++ Zune.Net.MetaServices/Controllers/FAI.cs | 49 ++++++++ Zune.Net.MetaServices/Controllers/ZuneApi.cs | 27 ----- Zune.Net.MetaServices/Controllers/redir.cs | 15 +++ Zune.Net.MetaServices/Dockerfile | 4 +- .../DomainModels/Endpoints/Endpoint.cs | 8 ++ .../DomainModels/Endpoints/Endpoints.cs | 11 ++ .../DomainModels/Endpoints/Metadata.cs | 38 ++++++ .../MDSR/ExtractedFromUIX/Album.cs | 18 +++ .../MDSR/ExtractedFromUIX/AlbumDetails.cs | 14 +++ .../ExtractedFromUIX/AlbumDetailsTrack.cs | 10 ++ .../MDSR/ExtractedFromUIX/AlbumList.cs | 9 ++ .../MDSR/ExtractedFromUIX/Artist.cs | 16 +++ .../MDSR/ExtractedFromUIX/ArtistList.cs | 10 ++ .../MDSR/ExtractedFromUIX/Track.cs | 22 ++++ .../MDSR/ExtractedFromUIX/TrackList.cs | 9 ++ .../DomainModels/MDSR/MDSRCD.cs | 15 +++ .../DomainModels/MDSR/Metadata.cs | 14 +++ .../DomainModels/MDSR/Result.cs | 37 ++++++ .../DomainModels/MDSR/SearchResult.cs | 12 ++ .../DomainModels/MDSR/Track.cs | 19 +++ Zune.Net.MetaServices/Models/WMIS.cs | 112 ++++++++++++++++++ Zune.Net.MetaServices/Program.cs | 6 - .../Zune.Net.MetaServices.csproj | 1 + Zune.Net.Mix/Dockerfile | 4 +- Zune.Net.SocialApi/Dockerfile | 4 +- Zune.Net.Tiles/Dockerfile | 4 +- Zune.Net.Tiles/Zune.Net.Tiles.csproj | 9 +- Zune.Net.Tuners/Dockerfile | 4 +- Zune.Net.Tuners/Program.cs | 5 +- nginx/nginx.conf | 32 ++++- 41 files changed, 593 insertions(+), 65 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 Zune.Net.MetaServices/Controllers/Cover.cs create mode 100644 Zune.Net.MetaServices/Controllers/FAI.cs delete mode 100644 Zune.Net.MetaServices/Controllers/ZuneApi.cs create mode 100644 Zune.Net.MetaServices/Controllers/redir.cs create mode 100644 Zune.Net.MetaServices/DomainModels/Endpoints/Endpoint.cs create mode 100644 Zune.Net.MetaServices/DomainModels/Endpoints/Endpoints.cs create mode 100644 Zune.Net.MetaServices/DomainModels/Endpoints/Metadata.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Album.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetails.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetailsTrack.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumList.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Artist.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/ArtistList.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Track.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/TrackList.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/MDSRCD.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/Metadata.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/Result.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/SearchResult.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/Track.cs create mode 100644 Zune.Net.MetaServices/Models/WMIS.cs diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..570f853 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/Zune.Net.Catalog/bin/Debug/net7.0/Zune.Net.Catalog.dll", + "args": [], + "cwd": "${workspaceFolder}/Zune.Net.Catalog", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + // "serverReadyAction": { + // "action": "openExternally", + // "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + // }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..87562fb --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Zune.Net.Catalog/Zune.Net.Catalog.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/Zune.Net.Catalog/Zune.Net.Catalog.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/Zune.Net.Catalog/Zune.Net.Catalog.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/Zune.Net.Catalog.Image/Dockerfile b/Zune.Net.Catalog.Image/Dockerfile index 6eb0a00..f2f6af5 100644 --- a/Zune.Net.Catalog.Image/Dockerfile +++ b/Zune.Net.Catalog.Image/Dockerfile @@ -6,11 +6,11 @@ WORKDIR /source COPY ./ . RUN dotnet restore -RUN dotnet publish -c release -o /app --no-restore +RUN dotnet publish -c release -p:PublishDir=./publish --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app/Zune.Net.Catalog.Image ./ +COPY --from=build /source/Zune.Net.Catalog.Image/publish/ ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Catalog.Image.dll"] \ No newline at end of file diff --git a/Zune.Net.Catalog/Controllers/Music/AlbumController.cs b/Zune.Net.Catalog/Controllers/Music/AlbumController.cs index e6174be..299b003 100644 --- a/Zune.Net.Catalog/Controllers/Music/AlbumController.cs +++ b/Zune.Net.Catalog/Controllers/Music/AlbumController.cs @@ -20,7 +20,7 @@ public ActionResult> Search() return MusicBrainz.SearchAlbums(queries[0], Request.Path); } - [HttpGet, Route("{mbid}")] + [HttpGet("{mbid}"), HttpGet("details/{mbid}")] public ActionResult Details(Guid mbid) { return MusicBrainz.GetAlbumByMBID(mbid); diff --git a/Zune.Net.Catalog/Dockerfile b/Zune.Net.Catalog/Dockerfile index bb32f44..f71669b 100644 --- a/Zune.Net.Catalog/Dockerfile +++ b/Zune.Net.Catalog/Dockerfile @@ -6,11 +6,11 @@ WORKDIR /source COPY ./ . RUN dotnet restore -RUN dotnet publish -c release -o /app --no-restore +RUN dotnet publish -c release -p:PublishDir=./publish --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app/Zune.Net.Catalog ./ +COPY --from=build /source/Zune.Net.Catalog/publish/ ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Catalog.dll"] diff --git a/Zune.Net.Commerce/Dockerfile b/Zune.Net.Commerce/Dockerfile index 2f13ac8..d1241f0 100644 --- a/Zune.Net.Commerce/Dockerfile +++ b/Zune.Net.Commerce/Dockerfile @@ -6,11 +6,11 @@ WORKDIR /source COPY ./ . RUN dotnet restore -RUN dotnet publish -c release -o /app --no-restore +RUN dotnet publish -c release -p:PublishDir=./publish --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app/Zune.Net.Commerce ./ +COPY --from=build /source/Zune.Net.Commerce/publish/ ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Commerce.dll"] diff --git a/Zune.Net.Commerce/appsettings.Development.json b/Zune.Net.Commerce/appsettings.Development.json index dc23838..4bdf8bb 100644 --- a/Zune.Net.Commerce/appsettings.Development.json +++ b/Zune.Net.Commerce/appsettings.Development.json @@ -1,7 +1,10 @@ { + "ZuneNetContext": { + "ConnectionString": "mongodb://root:rootpassword@localhost:27017" + }, "Logging": { "LogLevel": { "Default": "Debug" } } -} +} \ No newline at end of file diff --git a/Zune.Net.Inbox/Controllers/MessagingInboxController.cs b/Zune.Net.Inbox/Controllers/MessagingInboxController.cs index 9847e58..b14f1a9 100644 --- a/Zune.Net.Inbox/Controllers/MessagingInboxController.cs +++ b/Zune.Net.Inbox/Controllers/MessagingInboxController.cs @@ -56,7 +56,8 @@ public ActionResult Details(string locale, string zuneTag, strin return message; } - [HttpGet] + [HttpGet("unreadcount")] + [HttpGet("UnreadCount")] public IActionResult UnreadCont(string locale, string zuneTag) { diff --git a/Zune.Net.Inbox/Dockerfile b/Zune.Net.Inbox/Dockerfile index b791534..5a3d99f 100644 --- a/Zune.Net.Inbox/Dockerfile +++ b/Zune.Net.Inbox/Dockerfile @@ -6,11 +6,11 @@ WORKDIR /source COPY ./ . RUN dotnet restore -RUN dotnet publish -c release -o /app --no-restore +RUN dotnet publish -c release -p:PublishDir=./publish --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app/Zune.Net.Inbox ./ +COPY --from=build /source/Zune.Net.Inbox/publish/ ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Inbox.dll"] diff --git a/Zune.Net.Login/Dockerfile b/Zune.Net.Login/Dockerfile index e9cc888..9289e84 100644 --- a/Zune.Net.Login/Dockerfile +++ b/Zune.Net.Login/Dockerfile @@ -6,11 +6,11 @@ WORKDIR /source COPY ./ . RUN dotnet restore -RUN dotnet publish -c release -o /app --no-restore +RUN dotnet publish -c release -p:PublishDir=./publish --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app/Zune.Net.Login ./ +COPY --from=build /source/Zune.Net.Login/publish/ ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Login.dll"] diff --git a/Zune.Net.MetaServices/Controllers/Cover.cs b/Zune.Net.MetaServices/Controllers/Cover.cs new file mode 100644 index 0000000..3e779a3 --- /dev/null +++ b/Zune.Net.MetaServices/Controllers/Cover.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Zune.Net.MetaServices.Controllers +{ + [Route("/Cover/")] + public class Cover : Controller + { + [Route("{coverId}")] + public async Task GetCover(Guid coverId) + { + return NotFound(); + } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/Controllers/FAI.cs b/Zune.Net.MetaServices/Controllers/FAI.cs new file mode 100644 index 0000000..6e2e559 --- /dev/null +++ b/Zune.Net.MetaServices/Controllers/FAI.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Mvc; +using Zune.Net.Helpers; +using Zune.Net.MetaServices.DomainModels.Endpoints; + +namespace Zune.Net.MetaServices.Controllers +{ + [Route("/ZuneFAI/")] + public class FAI : Controller + { + [HttpGet("Search")] + [Produces("application/xml")] + public async Task Search(string SearchString, string resultTypeString) + { + // var results = await MusicBrainz.MDARSearchAlbums(SearchString); + // //resultTypeString album or artist + // return results[0]; + switch (resultTypeString) + { + case "album": + // if SearchString, not specific, but artistId is specific to artist + // return Ok(new AlbumList{ + // ReturnCode = "SUCCESS", + // Items = await MusicBrainz.SearchForAlbums(SearchString) + // }); + return Ok(await WMIS.SearchAlbums(SearchString)); + // case "artist": + // return Ok(new ArtistList(){ + // ReturnCode = "SUCCESS", + // Items = new List() + // }); + // case "track": + // return Ok(new TrackList() + // { + // ReturnCode = "SUCCESS", + // Items = new List() + // }); + } + return NotFound(); + } + + // Also known as WMISFAIGetAlbumsByArtistQuery in the UIX side + [HttpGet("GetAlbumDetailsFromAlbumId")] + [Produces("application/xml")] + public async Task MDARGetAlbumDetailsFromAlbumId() + { + return Ok(); + } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/Controllers/ZuneApi.cs b/Zune.Net.MetaServices/Controllers/ZuneApi.cs deleted file mode 100644 index efd152a..0000000 --- a/Zune.Net.MetaServices/Controllers/ZuneApi.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Atom; -using Microsoft.AspNetCore.Mvc; - -namespace Zune.Net.MetaServices.Controllers -{ - [ApiController] - [Route("/ZuneAPI/")] - public class ZuneApi : ControllerBase - { - private readonly ILogger _logger; - - public ZuneApi(ILogger logger) - { - _logger = logger; - } - - [HttpGet, Route("EndPoints.aspx"), Route("EndPoints")] - public IActionResult EndPoints() - { - return Content(@" - - - -", Constants.XML_MIMETYPE); - } - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/Controllers/redir.cs b/Zune.Net.MetaServices/Controllers/redir.cs new file mode 100644 index 0000000..4f17fef --- /dev/null +++ b/Zune.Net.MetaServices/Controllers/redir.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc; +using Zune.Net.MetaServices.DomainModels.Endpoints; + +namespace Zune.Net.MetaServices.Controllers +{ + [ApiController] + [Route("/redir/")] + public class Redir : ControllerBase{ + [HttpGet("ZuneFAI/")] + public IActionResult ZuneFai() + { + return Ok(new Metadata()); + } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/Dockerfile b/Zune.Net.MetaServices/Dockerfile index b625ead..79d7246 100644 --- a/Zune.Net.MetaServices/Dockerfile +++ b/Zune.Net.MetaServices/Dockerfile @@ -6,11 +6,11 @@ WORKDIR /source COPY ./ . RUN dotnet restore -RUN dotnet publish -c release -o /app --no-restore +RUN dotnet publish -c release -p:PublishDir=./publish --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app/Zune.Net.MetaServices ./ +COPY --from=build /source/Zune.Net.MetaServices/publish/ ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.MetaServices.dll"] diff --git a/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoint.cs b/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoint.cs new file mode 100644 index 0000000..e8a1190 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoint.cs @@ -0,0 +1,8 @@ +namespace Zune.Net.MetaServices.DomainModels.Endpoints +{ + public class Endpoint + { + public string Name; + public string Uri; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoints.cs b/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoints.cs new file mode 100644 index 0000000..d907d59 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoints.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.Endpoints +{ + public class Endpoints + { + [XmlElement("ENDPOINT")] + public List endpoints; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/Endpoints/Metadata.cs b/Zune.Net.MetaServices/DomainModels/Endpoints/Metadata.cs new file mode 100644 index 0000000..a207fc2 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/Endpoints/Metadata.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.Endpoints +{ + [XmlRoot("METADATA")] + public class Metadata + { + public Metadata() + { + var baseUrl = "http://metaservices.zune.net/ZuneFAI/"; + var endpointList = new List() + { + "Search", "GetAlbumDetailsByToc", "SubmitMatchFeedback", "GetAlbumDetailsFromToc", + "GetAlbumDetails", "GetAlbumLiteByTrackWMname", "GetAlbumLiteByToc", "GetAlbumDetailsFromAlbumId", + "GetAlbumDetailsByAlbumId", "SubmitEditFeedback", "SubmitAddFeedback", "GetResultsForArtist" + }; + + var _endpoints = new List(); + + foreach(var endpoint in endpointList) + { + _endpoints.Add(new Endpoint() + { + Name = endpoint, + Uri = $"{baseUrl}{endpoint}" + }); + } + + endpoints = new Endpoints() + { + endpoints = _endpoints + }; + } + [XmlElement("ENDPOINTS")] + public Endpoints endpoints; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Album.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Album.cs new file mode 100644 index 0000000..73d509d --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Album.cs @@ -0,0 +1,18 @@ +using System; + +namespace Zune.Xml.MDAR +{ + public class Album{ + public string Title; + public Int64 Id; //not requred + public string AlbumArtist; + public string Genre; + public int Volume; + public DateTime ReleaseDate; + public int NumberOfTracks; + public Boolean BestMatch; + public Boolean IsMultiDisk; + public string CoverParams; + public string BuyNowParams; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetails.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetails.cs new file mode 100644 index 0000000..497a1ab --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetails.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace Zune.Xml.MDAR +{ + public class AlbumDetails + { + public string Title; + public Int64 AlbumId; + public int Volume; + public List Items; + public string ReturnCode; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetailsTrack.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetailsTrack.cs new file mode 100644 index 0000000..425eca6 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetailsTrack.cs @@ -0,0 +1,10 @@ +namespace Zune.Xml.MDAR +{ + public class AlbumDetailsTrack + { + public string Title; + public string Performers; + public int TrackNumber; + } + +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumList.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumList.cs new file mode 100644 index 0000000..6d7384f --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumList.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Zune.Xml.MDAR +{ + public class AlbumList{ + public List Items; + public string ReturnCode; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Artist.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Artist.cs new file mode 100644 index 0000000..345e0e5 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Artist.cs @@ -0,0 +1,16 @@ +using System; + +namespace Zune.Xml.MDAR +{ + // Artist List should return a list of these + public class Artist + { + public string ArtistName; + public Int64 Id; + public string ImageParams; + public int AlbumCount; + public Boolean BestMatch; + public int TrackCount; + public string Genre; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/ArtistList.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/ArtistList.cs new file mode 100644 index 0000000..e968717 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/ArtistList.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Zune.Xml.MDAR +{ + public class ArtistList + { + public List Items; + public string ReturnCode; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Track.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Track.cs new file mode 100644 index 0000000..e9fb4b9 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Track.cs @@ -0,0 +1,22 @@ +using System; + +namespace Zune.Xml.MDAR +{ + public class Track + { + public string Title; + public Int64 TrackId; + public Int64 AlbumId; + public string Genre; + public int Volume; + public DateTime ReleaseDate; + public int NumberOfTracks; + public Boolean BestMatch; + public Boolean IsMultiDisk; + public int TrackNumber; + public string CoverParams; + public string BuyNowParams; + public string AlbumArtist; + public string AlbumTitle; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/TrackList.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/TrackList.cs new file mode 100644 index 0000000..6220d74 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/TrackList.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Zune.Xml.MDAR +{ + public class TrackList{ + public List Items; + public string ReturnCode; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/MDSRCD.cs b/Zune.Net.MetaServices/DomainModels/MDSR/MDSRCD.cs new file mode 100644 index 0000000..caa6735 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/MDSRCD.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MDSR +{ + public class MDSRCD + { + [XmlElement] + public string ReturnCode { get; set; } = "SUCCESS"; // default to success, we don't know what nothing looks like. I've seen rumors of an empty MDAR-CD element. + + [XmlElement] + public SearchResult SearchResult { get; set; } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/Metadata.cs b/Zune.Net.MetaServices/DomainModels/MDSR/Metadata.cs new file mode 100644 index 0000000..07b7f49 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/Metadata.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MDSR +{ + // irritating name to make it generate correctly + [XmlRoot("METADATA")] + public class MDSRCDMetadata + { + [XmlElement("MDSR-CD")] // of type (UIX) Album + public MDSRCD mDSRcD {get; set;} + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/Result.cs b/Zune.Net.MetaServices/DomainModels/MDSR/Result.cs new file mode 100644 index 0000000..6b6630d --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/Result.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MDSR +{ + public class Result + { + + [XmlElement] + public bool bestmatch { get; set; } = false; + + [XmlElement] + public Int64 album_id { get; set; } = 0; + [XmlElement] + public int Volume { get; set; } = 0; + + [XmlElement] + public string albumPerformer { get; set; } = string.Empty; // Album Artist + + [XmlElement] + public string albumCover { get; set; } = string.Empty; // probably a URL. Does nothing so far + [XmlElement] + public string buyNowLink { get; set; } = string.Empty; // redirects to http://social.zune.net/album/FindAlbum.aspx?&culture=en-US + + [XmlElement] + public string albumReleaseDate { get; set; } = string.Empty; // yyyy-mm-dd + + [XmlElement] + public string albumGenre { get; set; } = string.Empty; // i.e. dance + + [XmlElement] + public bool IsMultiDisk { get; set; } = false; + [XmlElement] + public int numberOfTracks {get; set;} = 0; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/SearchResult.cs b/Zune.Net.MetaServices/DomainModels/MDSR/SearchResult.cs new file mode 100644 index 0000000..468a2f4 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/SearchResult.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MDSR +{ + public class SearchResult + { + [XmlElement("Result")] + public List Results; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/Track.cs b/Zune.Net.MetaServices/DomainModels/MDSR/Track.cs new file mode 100644 index 0000000..6e0f466 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR/Track.cs @@ -0,0 +1,19 @@ +using System; + +namespace Zune.Net.MetaServices.DomainModels.MDSR +{ + public class MDARTrack + { + public int TrackRequestId {get; set;} = -1; // -1 in example + public Guid TrackWmid {get; set;} = new Guid(); + public string TrackNum {get; set;} = string.Empty; + public string Title {get; set;} = string.Empty; + public string Duration {get; set;} = string.Empty; // mm:ss + public string UniqueFIleId {get; set;} = string.Empty; //AMGp_id=P 3812;AMGt_id=T 4766848 + public string Performers {get; set;} = string.Empty; + public string Composers {get; set;} = string.Empty; + public string Conductors {get; set;} = string.Empty; + public string Period {get; set;} = string.Empty; //???? + public bool ExplicitLyrics = false; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/Models/WMIS.cs b/Zune.Net.MetaServices/Models/WMIS.cs new file mode 100644 index 0000000..a187c40 --- /dev/null +++ b/Zune.Net.MetaServices/Models/WMIS.cs @@ -0,0 +1,112 @@ +using MetaBrainz.MusicBrainz; +using Zune.Net.MetaServices.DomainModels.MDSR; + +namespace Zune.Net.Helpers +{ + public static class WMIS + { + public static readonly Query _query = new("Zune", "4.8", "https://github.com/xerootg/ZuneNetApi"); + // public static async Task> SearchForAlbums(string query) + // { + // var results = await _query.FindReleasesAsync(query, simple: true, limit: 1); + // var releases = results.Results.Select(x => x.Item).ToList(); + // var ret = new List(); + // foreach (var album in releases) + // { + // var deepRelease = await _query.LookupReleaseAsync(album.Id, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); + // var genre = "Unknown"; + // if (deepRelease.Genres?.Count > 0) + // { + // genre = deepRelease.Genres[0].Name; + // } + // var releaseDate = DateTime.Now; + // if (deepRelease.Date != null) + // { + // releaseDate = new DateTime(deepRelease.Date.Year ?? DateTime.Now.Year, deepRelease.Date.Month ?? DateTime.Now.Month, deepRelease.Date.Day ?? DateTime.Now.Day); + // } + // try + // { + // ret.Add(new Album() + // { + // Title = deepRelease.Title, + // Id = 1, + // AlbumArtist = deepRelease.ArtistCredit[0].Name, + // Genre = genre, + // Volume = 1, + // ReleaseDate = releaseDate, + // NumberOfTracks = deepRelease.Media[0].TrackCount, + // BestMatch = (int)results.Results.Where(x => x.Item.Id == album.Id).First().Score == 100, + // IsMultiDisk = deepRelease.Media.Count > 1, + // CoverParams = string.Empty, + // BuyNowParams = string.Empty + // }); + // } + // catch { } + // } + // return ret; + // } + + public static async Task SearchAlbums(string query) + { + var results = await _query.FindReleasesAsync(query, simple: true, limit: 10); + var releases = results.Results.Select(x => x.Item).ToList(); + var resultList = new List(); + foreach (var release in releases) + { + var deepRelease = await _query.LookupReleaseAsync(release.Id, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); + + var label = string.Empty; + var genre = string.Empty; + var performerName = string.Empty; + var artistGuid = Guid.Empty; + var releaseDate = $"{DateTime.Now.Year}-{DateTime.Now.Month}-{DateTime.Now.Day}"; + if (deepRelease.Genres?.Count > 0) + { + genre = deepRelease.Genres[0].Name; + } + if (deepRelease.LabelInfo?.Count > 0) + { + label = deepRelease.LabelInfo[0].Label?.Name ?? deepRelease.LabelInfo[0].CatalogNumber; + } + if (deepRelease.ArtistCredit?.Count > 0) + { + performerName = deepRelease.ArtistCredit?[0].Artist.Name; + artistGuid = deepRelease.ArtistCredit[0].Artist.Id; + } + if (deepRelease.Date != null) + { + releaseDate = $"{deepRelease.Date.Year ?? DateTime.Now.Year}-{deepRelease.Date.Month ?? DateTime.Now.Month}-{deepRelease.Date.Day ?? DateTime.Now.Day}"; + } + + var result = new Result() + { + bestmatch = (int)results.Results.Where(x => x.Item.Id == deepRelease.Id).First().Score == 100, + album_id = int.Parse(release.Id.ToString("N")[0..6], System.Globalization.NumberStyles.HexNumber), + Volume = 1, + albumPerformer = performerName, + buyNowLink = "http://google.com", + albumReleaseDate = releaseDate, + albumGenre = genre, + numberOfTracks = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media[0].TrackCount : 0, + IsMultiDisk = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media.Count > 1 : false, + albumCover = release.Id.ToString() + }; + resultList.Add(result); + } + + // How's that for a stackup? + var ret = new MDSRCDMetadata() + { + mDSRcD = new MDSRCD() + { + SearchResult = new SearchResult() + { + Results = resultList + } + } + }; + + return ret; + } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/Program.cs b/Zune.Net.MetaServices/Program.cs index ac7c10a..0b7712a 100644 --- a/Zune.Net.MetaServices/Program.cs +++ b/Zune.Net.MetaServices/Program.cs @@ -6,12 +6,6 @@ var app = builder.Build(); -// Configure the HTTP request pipeline. - -app.UseHttpsRedirection(); - -app.UseAuthorization(); - app.MapControllers(); app.Run(); diff --git a/Zune.Net.MetaServices/Zune.Net.MetaServices.csproj b/Zune.Net.MetaServices/Zune.Net.MetaServices.csproj index 5ba21c1..7595f91 100644 --- a/Zune.Net.MetaServices/Zune.Net.MetaServices.csproj +++ b/Zune.Net.MetaServices/Zune.Net.MetaServices.csproj @@ -8,6 +8,7 @@ + diff --git a/Zune.Net.Mix/Dockerfile b/Zune.Net.Mix/Dockerfile index ae50032..1eae54c 100644 --- a/Zune.Net.Mix/Dockerfile +++ b/Zune.Net.Mix/Dockerfile @@ -6,11 +6,11 @@ WORKDIR /source COPY ./ . RUN dotnet restore -RUN dotnet publish -c release -o /app --no-restore +RUN dotnet publish -c release -p:PublishDir=./publish --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app/Zune.Net.Mix ./ +COPY --from=build /source/Zune.Net.Mix/publish/ ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Mix.dll"] diff --git a/Zune.Net.SocialApi/Dockerfile b/Zune.Net.SocialApi/Dockerfile index 03ae370..d562e39 100644 --- a/Zune.Net.SocialApi/Dockerfile +++ b/Zune.Net.SocialApi/Dockerfile @@ -6,11 +6,11 @@ WORKDIR /source COPY ./ . RUN dotnet restore -RUN dotnet publish -c release -o /app --no-restore +RUN dotnet publish -c release -p:PublishDir=./publish --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app/Zune.Net.SocialApi ./ +COPY --from=build /source/Zune.Net.SocialApi/publish/ ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.SocialApi.dll"] diff --git a/Zune.Net.Tiles/Dockerfile b/Zune.Net.Tiles/Dockerfile index e4989de..58e8eef 100644 --- a/Zune.Net.Tiles/Dockerfile +++ b/Zune.Net.Tiles/Dockerfile @@ -6,11 +6,11 @@ WORKDIR /source COPY ./ . RUN dotnet restore -RUN dotnet publish -c release -o /app --no-restore +RUN dotnet publish -c release -p:PublishDir=./publish --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app/Zune.Net.Tiles ./ +COPY --from=build /source/Zune.Net.Tiles/publish/ ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Tiles.dll"] diff --git a/Zune.Net.Tiles/Zune.Net.Tiles.csproj b/Zune.Net.Tiles/Zune.Net.Tiles.csproj index b2da86a..22170ca 100644 --- a/Zune.Net.Tiles/Zune.Net.Tiles.csproj +++ b/Zune.Net.Tiles/Zune.Net.Tiles.csproj @@ -6,8 +6,11 @@ enable - - - + + + PreserveNewest + Always + + diff --git a/Zune.Net.Tuners/Dockerfile b/Zune.Net.Tuners/Dockerfile index eb993c6..65baf2a 100644 --- a/Zune.Net.Tuners/Dockerfile +++ b/Zune.Net.Tuners/Dockerfile @@ -6,11 +6,11 @@ WORKDIR /source COPY ./ . RUN dotnet restore -RUN dotnet publish -c release -o /app --no-restore +RUN dotnet publish -c release -p:PublishDir=./publish --no-restore # final stage/image FROM mcr.microsoft.com/dotnet/aspnet:7.0 WORKDIR /app -COPY --from=build /app/Zune.Net.Tuners ./ +COPY --from=build /source/Zune.Net.Tuners/publish/ ./ ENV DOTNET_EnableDiagnostics=0 ENTRYPOINT ["dotnet", "Zune.Net.Tuners.dll"] diff --git a/Zune.Net.Tuners/Program.cs b/Zune.Net.Tuners/Program.cs index 54c86dc..4e477c3 100644 --- a/Zune.Net.Tuners/Program.cs +++ b/Zune.Net.Tuners/Program.cs @@ -4,13 +4,10 @@ var app = builder.Build(); -// Configure the HTTP request pipeline. - -app.UseHttpsRedirection(); - app.MapGet("/{locale}/ZunePCClient/{version}/{file}.xml", (string locale, string version, string file) => { string filePath = Path.Combine(app.Environment.ContentRootPath, "Resources", file + ".xml"); + Console.WriteLine($"Serving up {filePath}"); string zuneConfig = File.ReadAllText(filePath); return zuneConfig; diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 4093e58..313ccac 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -5,6 +5,20 @@ events { worker_connections 1024; } http { sendfile on; + + server{ + listen 80; + server_name toc.music.metaservices.microsoft.com metaservices.zune.net fai.music.metaservices.microsoft.com redir.metaservices.microsoft.com images.metaservices.microsoft.com; + location / { + proxy_pass http://metaservices; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + expires -1; + } + } server { listen 80; @@ -16,6 +30,7 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; + expires -1; } } @@ -29,6 +44,7 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; + expires -1; } } @@ -43,6 +59,7 @@ http { location / { proxy_pass http://commerce; proxy_redirect off; + expires -1; } } @@ -60,6 +77,7 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; + expires -1; } } @@ -73,12 +91,17 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; + expires -1; } } server { listen 80; - server_name tuners.zune.net; + listen 443 ssl; + server_name tuners.zune.net tuners.zunes.me; + + ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt; + ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key; location / { proxy_pass http://tuners; proxy_redirect off; @@ -86,6 +109,7 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; + expires -1; } } @@ -99,6 +123,7 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; + expires -1; } } @@ -116,6 +141,7 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; + expires -1; } } @@ -133,6 +159,7 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; + expires -1; } } @@ -146,6 +173,7 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; + expires -1; } } -} \ No newline at end of file +} From ff27ec172566885de9ca3b44f212a8133db07224 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Sat, 29 Apr 2023 09:54:17 -0600 Subject: [PATCH 13/29] A bit of reorganization now that MetaServices is running also Parallel.ForEachAsync is cool. also albumart sort of loads --- .vscode/launch.json | 4 +- .vscode/tasks.json | 6 +- Zune.Net.Commerce/Program.cs | 2 - Zune.Net.MetaServices/Controllers/Cover.cs | 26 +++++++- Zune.Net.MetaServices/Controllers/FAI.cs | 7 ++- Zune.Net.MetaServices/Controllers/redir.cs | 1 + Zune.Net.MetaServices/Models/WMIS.cs | 63 ++++++++++++++----- Zune.Net.MetaServices/Program.cs | 7 ++- .../appsettings.Development.json | 4 ++ Zune.Net.MetaServices/appsettings.json | 4 ++ runDev.sh | 4 ++ runProd.sh | 4 ++ 12 files changed, 105 insertions(+), 27 deletions(-) create mode 100755 runDev.sh create mode 100755 runProd.sh diff --git a/.vscode/launch.json b/.vscode/launch.json index 570f853..cabae57 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,9 +10,9 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/Zune.Net.Catalog/bin/Debug/net7.0/Zune.Net.Catalog.dll", + "program": "${workspaceFolder}/Zune.Net.MetaServices/bin/Debug/net7.0/Zune.Net.MetaServices.dll", "args": [], - "cwd": "${workspaceFolder}/Zune.Net.Catalog", + "cwd": "${workspaceFolder}/Zune.Net.MetaServices", "stopAtEntry": false, // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser // "serverReadyAction": { diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 87562fb..cb86632 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,7 +7,7 @@ "type": "process", "args": [ "build", - "${workspaceFolder}/Zune.Net.Catalog/Zune.Net.Catalog.csproj", + "${workspaceFolder}/Zune.Net.MetaServices/Zune.Net.MetaServices.csproj", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], @@ -19,7 +19,7 @@ "type": "process", "args": [ "publish", - "${workspaceFolder}/Zune.Net.Catalog/Zune.Net.Catalog.csproj", + "${workspaceFolder}/Zune.Net.MetaServices/Zune.Net.MetaServices.csproj", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], @@ -33,7 +33,7 @@ "watch", "run", "--project", - "${workspaceFolder}/Zune.Net.Catalog/Zune.Net.Catalog.csproj" + "${workspaceFolder}/Zune.Net.MetaServices/Zune.Net.MetaServices.csproj" ], "problemMatcher": "$msCompile" } diff --git a/Zune.Net.Commerce/Program.cs b/Zune.Net.Commerce/Program.cs index 14dcd99..c385da0 100644 --- a/Zune.Net.Commerce/Program.cs +++ b/Zune.Net.Commerce/Program.cs @@ -2,8 +2,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Formatters; -using Microsoft.AspNetCore.Mvc.Formatters.Xml; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; diff --git a/Zune.Net.MetaServices/Controllers/Cover.cs b/Zune.Net.MetaServices/Controllers/Cover.cs index 3e779a3..381dd67 100644 --- a/Zune.Net.MetaServices/Controllers/Cover.cs +++ b/Zune.Net.MetaServices/Controllers/Cover.cs @@ -5,10 +5,32 @@ namespace Zune.Net.MetaServices.Controllers [Route("/Cover/")] public class Cover : Controller { + private readonly ILogger _logger; + public Cover(ILogger logger) + { + _logger = logger; + } + private static readonly Uri defaultImage = new Uri("https://hellofromseattle.com/wp-content/uploads/sites/6/2020/03/Zune-Basic.png"); + [Route("{coverId}")] - public async Task GetCover(Guid coverId) + public async Task GetCover(string coverId) { - return NotFound(); + var catalogImageUri = new Uri($"https://coverartarchive.org/release/{coverId}/front-250"); + if(coverId.Equals("default")) + { + _logger.LogInformation($"default albumart requested"); + catalogImageUri = defaultImage; + } + + using var client = new HttpClient(); + var result = await client.GetAsync(catalogImageUri); + if(!result.IsSuccessStatusCode) + { + _logger.LogInformation($"Falling back to the default image with status {result.StatusCode}, failed to resolve actual artwork"); + result = await client.GetAsync(defaultImage); + } + return new HttpResponseMessageResult(result); + //HttpResponseMessageResult } } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/Controllers/FAI.cs b/Zune.Net.MetaServices/Controllers/FAI.cs index 6e2e559..efe4eeb 100644 --- a/Zune.Net.MetaServices/Controllers/FAI.cs +++ b/Zune.Net.MetaServices/Controllers/FAI.cs @@ -7,6 +7,11 @@ namespace Zune.Net.MetaServices.Controllers [Route("/ZuneFAI/")] public class FAI : Controller { + private readonly WMIS _wmis; + public FAI(WMIS wmis) + { + _wmis = wmis; + } [HttpGet("Search")] [Produces("application/xml")] public async Task Search(string SearchString, string resultTypeString) @@ -22,7 +27,7 @@ public async Task Search(string SearchString, string resultTypeStr // ReturnCode = "SUCCESS", // Items = await MusicBrainz.SearchForAlbums(SearchString) // }); - return Ok(await WMIS.SearchAlbums(SearchString)); + return Ok(await _wmis.SearchAlbums(SearchString)); // case "artist": // return Ok(new ArtistList(){ // ReturnCode = "SUCCESS", diff --git a/Zune.Net.MetaServices/Controllers/redir.cs b/Zune.Net.MetaServices/Controllers/redir.cs index 4f17fef..1c8e992 100644 --- a/Zune.Net.MetaServices/Controllers/redir.cs +++ b/Zune.Net.MetaServices/Controllers/redir.cs @@ -5,6 +5,7 @@ namespace Zune.Net.MetaServices.Controllers { [ApiController] [Route("/redir/")] + [Produces("application/xml")] public class Redir : ControllerBase{ [HttpGet("ZuneFAI/")] public IActionResult ZuneFai() diff --git a/Zune.Net.MetaServices/Models/WMIS.cs b/Zune.Net.MetaServices/Models/WMIS.cs index a187c40..197e473 100644 --- a/Zune.Net.MetaServices/Models/WMIS.cs +++ b/Zune.Net.MetaServices/Models/WMIS.cs @@ -1,11 +1,13 @@ +using System.Collections.Concurrent; using MetaBrainz.MusicBrainz; +using Zune.DB; using Zune.Net.MetaServices.DomainModels.MDSR; namespace Zune.Net.Helpers { - public static class WMIS + public class WMIS { - public static readonly Query _query = new("Zune", "4.8", "https://github.com/xerootg/ZuneNetApi"); + public static readonly Query Query = new("Zune", "4.8", "https://github.com/xerootg/ZuneNetApi"); // public static async Task> SearchForAlbums(string query) // { // var results = await _query.FindReleasesAsync(query, simple: true, limit: 1); @@ -45,21 +47,40 @@ public static class WMIS // } // return ret; // } + private readonly ZuneNetContext _database; + private readonly ILogger _logger; - public static async Task SearchAlbums(string query) + + public WMIS(ZuneNetContext database, ILogger logger) + { + _database = database; + _logger = logger; + } + + public async Task SearchAlbums(string query) { - var results = await _query.FindReleasesAsync(query, simple: true, limit: 10); + _logger.LogInformation($"Getting MDSR-CD results for AlbumSearch: {query}"); + var results = await Query.FindReleasesAsync(query, simple: true); var releases = results.Results.Select(x => x.Item).ToList(); - var resultList = new List(); - foreach (var release in releases) + + var resultList = new ConcurrentBag(); + + await Parallel.ForEachAsync(releases, async (release, ct) => { - var deepRelease = await _query.LookupReleaseAsync(release.Id, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); + if(ct.IsCancellationRequested) + { + return; + } + + _logger.LogInformation($"Getting all data for MBID: {release.Id}"); + var deepRelease = await Query.LookupReleaseAsync(release.Id, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); var label = string.Empty; - var genre = string.Empty; - var performerName = string.Empty; + var genre = "Unknown"; + var performerName = "Unknown Artist"; var artistGuid = Guid.Empty; var releaseDate = $"{DateTime.Now.Year}-{DateTime.Now.Month}-{DateTime.Now.Day}"; + var albumArtMbid = "default"; if (deepRelease.Genres?.Count > 0) { genre = deepRelease.Genres[0].Name; @@ -75,13 +96,21 @@ public static async Task SearchAlbums(string query) } if (deepRelease.Date != null) { + releaseDate = $"{deepRelease.Date.Year ?? DateTime.Now.Year}-{deepRelease.Date.Month ?? DateTime.Now.Month}-{deepRelease.Date.Day ?? DateTime.Now.Day}"; } + if(deepRelease.CoverArtArchive?.Front ?? false) + { + _logger.LogInformation($"Release {release.Id} HAS ARTWORK"); + albumArtMbid = release.Id.ToString(); + } + + //var id = _database.AddOrGetAlbumLookupRecordAsync(release.Id); - var result = new Result() + resultList.Add(new Result() { bestmatch = (int)results.Results.Where(x => x.Item.Id == deepRelease.Id).First().Score == 100, - album_id = int.Parse(release.Id.ToString("N")[0..6], System.Globalization.NumberStyles.HexNumber), + album_id = 123456789,//int.Parse(release.Id.ToString("N")[0..6], System.Globalization.NumberStyles.HexNumber), Volume = 1, albumPerformer = performerName, buyNowLink = "http://google.com", @@ -89,10 +118,12 @@ public static async Task SearchAlbums(string query) albumGenre = genre, numberOfTracks = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media[0].TrackCount : 0, IsMultiDisk = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media.Count > 1 : false, - albumCover = release.Id.ToString() - }; - resultList.Add(result); - } + albumCover = albumArtMbid + }); + _logger.LogInformation($"Finished building MDSR-CD result for MBID: {release.Id}"); + }); + + _logger.LogInformation($"Found {resultList.Count} results"); // How's that for a stackup? var ret = new MDSRCDMetadata() @@ -101,7 +132,7 @@ public static async Task SearchAlbums(string query) { SearchResult = new SearchResult() { - Results = resultList + Results = resultList.ToList() } } }; diff --git a/Zune.Net.MetaServices/Program.cs b/Zune.Net.MetaServices/Program.cs index 0b7712a..565bb3f 100644 --- a/Zune.Net.MetaServices/Program.cs +++ b/Zune.Net.MetaServices/Program.cs @@ -1,8 +1,13 @@ +using Zune.Net; +using Zune.Net.Helpers; + var builder = WebApplication.CreateBuilder(args); // Add services to the container. -builder.Services.AddControllers(); +builder.Services.AddControllers().AddXmlSerializerFormatters(); +builder.Services.AddSingleton(typeof(WMIS)); +builder.Host.ConfigureZuneDB(); var app = builder.Build(); diff --git a/Zune.Net.MetaServices/appsettings.Development.json b/Zune.Net.MetaServices/appsettings.Development.json index dc23838..1e11ded 100644 --- a/Zune.Net.MetaServices/appsettings.Development.json +++ b/Zune.Net.MetaServices/appsettings.Development.json @@ -1,4 +1,8 @@ { + "ZuneNetContext": { + "ConnectionString": "mongodb://root:rootpassword@localhost:27017", + "DatabaseName": "Zune" +}, "Logging": { "LogLevel": { "Default": "Debug" diff --git a/Zune.Net.MetaServices/appsettings.json b/Zune.Net.MetaServices/appsettings.json index 10f68b8..23cbe92 100644 --- a/Zune.Net.MetaServices/appsettings.json +++ b/Zune.Net.MetaServices/appsettings.json @@ -1,4 +1,8 @@ { + "ZuneNetContext": { + "ConnectionString": "mongodb://root:rootpassword@mongodb:27017", + "DatabaseName": "Zune" +}, "Logging": { "LogLevel": { "Default": "Information", diff --git a/runDev.sh b/runDev.sh new file mode 100755 index 0000000..f371ac6 --- /dev/null +++ b/runDev.sh @@ -0,0 +1,4 @@ +#!/bin/bash +docker-compose stop +docker-compose build +docker-compose up \ No newline at end of file diff --git a/runProd.sh b/runProd.sh new file mode 100755 index 0000000..2ba123a --- /dev/null +++ b/runProd.sh @@ -0,0 +1,4 @@ +#!/bin/bash +docker-compose stop +docker-compose build +docker-compose start #??? --prune-orphans \ No newline at end of file From b2c49d59d2cad8f87a6cbda1198f13f18924fbad Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Sun, 30 Apr 2023 23:39:02 -0600 Subject: [PATCH 14/29] adding the ability to lookup the silly int64 id later if we need it --- Zune.DB/Models/WMISAlbumIdEntry.cs | 21 +++++++ Zune.DB/ZuneNetContext.cs | 48 +++++++++++++++ Zune.DB/ZuneNetContextSettings.cs | 2 + Zune.Net.MetaServices/Controllers/FAI.cs | 16 +++-- Zune.Net.MetaServices/Controllers/redir.cs | 6 ++ .../DomainModels/MDQ-CD/MdqAlbum.cs | 13 ++++ .../DomainModels/MDQ-CD/MdqCd.cs | 16 +++++ .../MDQ-CD/MdqDescriptionElement.cs | 13 ++++ .../DomainModels/MDQ-CD/MdqRequestMetadata.cs | 11 ++++ .../DomainModels/MDQ-CD/MdqTrack.cs | 31 ++++++++++ Zune.Net.MetaServices/Models/WMIS.cs | 60 ++++++++++--------- Zune.Net.MetaServices/Program.cs | 8 ++- Zune.Net.Shared/Extensions.cs | 7 ++- runProd.sh | 4 +- 14 files changed, 218 insertions(+), 38 deletions(-) create mode 100644 Zune.DB/Models/WMISAlbumIdEntry.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqAlbum.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqCd.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqDescriptionElement.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqRequestMetadata.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs diff --git a/Zune.DB/Models/WMISAlbumIdEntry.cs b/Zune.DB/Models/WMISAlbumIdEntry.cs new file mode 100644 index 0000000..6e26b1d --- /dev/null +++ b/Zune.DB/Models/WMISAlbumIdEntry.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using MongoDB.Bson.Serialization.Attributes; + +namespace Zune.DB.Models +{ + public class WMISAlbumIdEntry + { + [BsonId] + public string Id; + public Int64 AlbumId {get; set;} + public Guid AlbumGuid {get; set;} + + public WMISAlbumIdEntry(Int64 id, Guid guid) + { + Id = $"{guid.ToString()}-{id}"; + AlbumId = id; + AlbumGuid = guid; + } + } +} \ No newline at end of file diff --git a/Zune.DB/ZuneNetContext.cs b/Zune.DB/ZuneNetContext.cs index 680dee0..897f43d 100644 --- a/Zune.DB/ZuneNetContext.cs +++ b/Zune.DB/ZuneNetContext.cs @@ -14,6 +14,7 @@ public class ZuneNetContext private readonly IMongoCollection _memberCollection; private readonly IMongoCollection _authCollection; private readonly IMongoCollection _imageCollection; + private readonly IMongoCollection _albumLookupCollection; public ZuneNetContext(IOptions dbSettings) : this(dbSettings.Value) { @@ -28,6 +29,7 @@ public ZuneNetContext(ZuneNetContextSettings dbSettings) _memberCollection = mongoDatabase.GetCollection(dbSettings.MemberCollectionName); _authCollection = mongoDatabase.GetCollection(dbSettings.AuthCollectionName); _imageCollection = mongoDatabase.GetCollection(dbSettings.ImageCollectionName); + _albumLookupCollection = mongoDatabase.GetCollection(dbSettings.AlbumLookupCollectionName); } public async Task> GetAsync(Expression> filter = null) => @@ -116,6 +118,52 @@ public async Task AddImageAsync(string url) } public Task ClearImagesAsync() => _imageCollection.DeleteManyAsync(_ => true); + + public async Task GetAlbumIdRecordAsync(Int64 id) + { + try + { + var existing = await _albumLookupCollection.FindAsync(x => x.AlbumId == id); + var record = await existing.SingleAsync(); + return record.AlbumGuid; + } catch + { + return null; + } + } + + public async Task GetAlbumIdRecordAsync(Guid id) + { + try + { + var existing = await _albumLookupCollection.FindAsync(x => x.AlbumGuid == id); + var record = await existing.SingleAsync(); + return record.AlbumId; + } catch + { + return null; + } + } + + // It's ugly, but it maps a MBID to an Int64 for WMIS's crazy lookup + public async Task CreateOrGetAlbumIdInt64Async(Guid guid) + { + var existing = await GetAlbumIdRecordAsync(guid); + if (existing.HasValue) + { + return existing.Value; + } + while (true) + { + var id = new Random().NextInt64(); + var found = await GetAlbumIdRecordAsync(id); + if (!found.HasValue) + { + await _albumLookupCollection.InsertOneAsync(new WMISAlbumIdEntry(id, guid)); + return id; + } + } + } } } diff --git a/Zune.DB/ZuneNetContextSettings.cs b/Zune.DB/ZuneNetContextSettings.cs index aa8cfb1..9e256f5 100644 --- a/Zune.DB/ZuneNetContextSettings.cs +++ b/Zune.DB/ZuneNetContextSettings.cs @@ -11,5 +11,7 @@ public class ZuneNetContextSettings public string AuthCollectionName { get; set; } = "Auth"; public string ImageCollectionName { get; set; } = "Images"; + + public string AlbumLookupCollectionName { get; set; } = "AlbumLookup"; } } diff --git a/Zune.Net.MetaServices/Controllers/FAI.cs b/Zune.Net.MetaServices/Controllers/FAI.cs index efe4eeb..e29e4eb 100644 --- a/Zune.Net.MetaServices/Controllers/FAI.cs +++ b/Zune.Net.MetaServices/Controllers/FAI.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Zune.Net.Helpers; using Zune.Net.MetaServices.DomainModels.Endpoints; +using Zune.Net.MetaServices.DomainModels.MdqCd; namespace Zune.Net.MetaServices.Controllers { @@ -8,9 +9,11 @@ namespace Zune.Net.MetaServices.Controllers public class FAI : Controller { private readonly WMIS _wmis; - public FAI(WMIS wmis) + private readonly ILogger _logger; + public FAI(WMIS wmis, ILogger logger) { _wmis = wmis; + _logger = logger; } [HttpGet("Search")] [Produces("application/xml")] @@ -27,7 +30,7 @@ public async Task Search(string SearchString, string resultTypeStr // ReturnCode = "SUCCESS", // Items = await MusicBrainz.SearchForAlbums(SearchString) // }); - return Ok(await _wmis.SearchAlbums(SearchString)); + return Ok(await _wmis.SearchAlbumsAsync(SearchString)); // case "artist": // return Ok(new ArtistList(){ // ReturnCode = "SUCCESS", @@ -44,10 +47,15 @@ public async Task Search(string SearchString, string resultTypeStr } // Also known as WMISFAIGetAlbumsByArtistQuery in the UIX side - [HttpGet("GetAlbumDetailsFromAlbumId")] + [HttpPost("GetAlbumDetailsFromAlbumId")] [Produces("application/xml")] - public async Task MDARGetAlbumDetailsFromAlbumId() + public async Task MDARGetAlbumDetailsFromAlbumId([FromBody]MdqRequestMetadata request, int albumId, int locale, int volume) { + _logger.LogInformation($"MDQ-CD request for Album: {request.MdqCd.Album.AlbumTitle.Text} by {request.MdqCd.Album.AlbumArtist.Text}"); + foreach(var track in request.MdqCd.Tracks) + { + _logger.LogInformation($"Track: {track.TrackNumber} - {track.Title.Text}: RequestID: {track.trackRequestId}"); + } return Ok(); } } diff --git a/Zune.Net.MetaServices/Controllers/redir.cs b/Zune.Net.MetaServices/Controllers/redir.cs index 1c8e992..fdab813 100644 --- a/Zune.Net.MetaServices/Controllers/redir.cs +++ b/Zune.Net.MetaServices/Controllers/redir.cs @@ -12,5 +12,11 @@ public IActionResult ZuneFai() { return Ok(new Metadata()); } + + [HttpGet("getmdrcdposturlbackgroundzune")] + public IActionResult mdrcdposturl() + { + return Ok(new Metadata()); + } } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqAlbum.cs b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqAlbum.cs new file mode 100644 index 0000000..8e83b28 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqAlbum.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdqCd +{ + public class MdqAlbum + { + [XmlElement("title")] + public MdqDescriptionElement AlbumTitle; + + [XmlElement("artist")] + public MdqDescriptionElement AlbumArtist; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqCd.cs b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqCd.cs new file mode 100644 index 0000000..4e60189 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqCd.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdqCd +{ + public class MdqCd + { + [XmlElement("mdqRequestID")] + public string MdqRequestId { get; set; } + + [XmlElement("album")] + public MdqAlbum Album { get; set; } + + [XmlElement("track")] + public List Tracks { get; set; } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqDescriptionElement.cs b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqDescriptionElement.cs new file mode 100644 index 0000000..4abcec9 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqDescriptionElement.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdqCd +{ + public class MdqDescriptionElement + { + [XmlElement("text")] + public string Text; + + [XmlArrayItem("word")] + public List Words; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqRequestMetadata.cs b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqRequestMetadata.cs new file mode 100644 index 0000000..7f5bd77 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqRequestMetadata.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdqCd +{ + [XmlRoot("METADATA")] + public partial class MdqRequestMetadata + { + [XmlElement("MDQ-CD")] + public MdqCd MdqCd { get; set; } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs new file mode 100644 index 0000000..2983112 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs @@ -0,0 +1,31 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdqCd +{ + public class MdqTrack + { + [XmlElement("title")] + public MdqDescriptionElement Title; + + [XmlElement("artist")] + public MdqDescriptionElement Artist; + + [XmlElement("trackNumber")] + public int TrackNumber; + + [XmlElement("trackDuration")] + public int TrackDurationMs; + + [XmlElement("filename")] + public string TrackFilename; + + [XmlElement("bitrate")] + public int Bitrate; + + [XmlElement("drmProtected")] + public int DRMProtected; + + [XmlElement("trackRequestID")] + public int trackRequestId; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/Models/WMIS.cs b/Zune.Net.MetaServices/Models/WMIS.cs index 197e473..9c0cbcf 100644 --- a/Zune.Net.MetaServices/Models/WMIS.cs +++ b/Zune.Net.MetaServices/Models/WMIS.cs @@ -7,7 +7,7 @@ namespace Zune.Net.Helpers { public class WMIS { - public static readonly Query Query = new("Zune", "4.8", "https://github.com/xerootg/ZuneNetApi"); + public readonly Query _query; // public static async Task> SearchForAlbums(string query) // { // var results = await _query.FindReleasesAsync(query, simple: true, limit: 1); @@ -51,39 +51,40 @@ public class WMIS private readonly ILogger _logger; - public WMIS(ZuneNetContext database, ILogger logger) + public WMIS(Query query, ZuneNetContext database, ILogger logger) { _database = database; _logger = logger; + _query = query; } - public async Task SearchAlbums(string query) + public async Task SearchAlbumsAsync(string query) { _logger.LogInformation($"Getting MDSR-CD results for AlbumSearch: {query}"); - var results = await Query.FindReleasesAsync(query, simple: true); + var results = await _query.FindReleasesAsync(query, simple: true); //, limit: 2); var releases = results.Results.Select(x => x.Item).ToList(); var resultList = new ConcurrentBag(); await Parallel.ForEachAsync(releases, async (release, ct) => { - if(ct.IsCancellationRequested) + if (ct.IsCancellationRequested) { return; } _logger.LogInformation($"Getting all data for MBID: {release.Id}"); - var deepRelease = await Query.LookupReleaseAsync(release.Id, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); + var deepRelease = await _query.LookupReleaseAsync(release.Id, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); var label = string.Empty; var genre = "Unknown"; var performerName = "Unknown Artist"; var artistGuid = Guid.Empty; - var releaseDate = $"{DateTime.Now.Year}-{DateTime.Now.Month}-{DateTime.Now.Day}"; + var releaseDate = DateTime.Now.ToString("o"); var albumArtMbid = "default"; if (deepRelease.Genres?.Count > 0) { - genre = deepRelease.Genres[0].Name; + genre = deepRelease.Genres?[0]?.Name; } if (deepRelease.LabelInfo?.Count > 0) { @@ -91,36 +92,37 @@ await Parallel.ForEachAsync(releases, async (release, ct) => } if (deepRelease.ArtistCredit?.Count > 0) { - performerName = deepRelease.ArtistCredit?[0].Artist.Name; + performerName = deepRelease.ArtistCredit?[0]?.Artist.Name; artistGuid = deepRelease.ArtistCredit[0].Artist.Id; } - if (deepRelease.Date != null) + if (deepRelease.Date != null && !deepRelease.Date.IsEmpty) { - - releaseDate = $"{deepRelease.Date.Year ?? DateTime.Now.Year}-{deepRelease.Date.Month ?? DateTime.Now.Month}-{deepRelease.Date.Day ?? DateTime.Now.Day}"; + + releaseDate = deepRelease.Date.NearestDate.ToString("o"); } - if(deepRelease.CoverArtArchive?.Front ?? false) + if (deepRelease.CoverArtArchive?.Front ?? false) { _logger.LogInformation($"Release {release.Id} HAS ARTWORK"); albumArtMbid = release.Id.ToString(); - } - //var id = _database.AddOrGetAlbumLookupRecordAsync(release.Id); - resultList.Add(new Result() - { - bestmatch = (int)results.Results.Where(x => x.Item.Id == deepRelease.Id).First().Score == 100, - album_id = 123456789,//int.Parse(release.Id.ToString("N")[0..6], System.Globalization.NumberStyles.HexNumber), - Volume = 1, - albumPerformer = performerName, - buyNowLink = "http://google.com", - albumReleaseDate = releaseDate, - albumGenre = genre, - numberOfTracks = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media[0].TrackCount : 0, - IsMultiDisk = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media.Count > 1 : false, - albumCover = albumArtMbid - }); - _logger.LogInformation($"Finished building MDSR-CD result for MBID: {release.Id}"); + var recordId = await _database.CreateOrGetAlbumIdInt64Async(release.Id); + + resultList.Add(new Result() + { + bestmatch = (int)results.Results.Where(x => x.Item.Id == deepRelease.Id).First().Score == 100, + album_id = recordId, + Volume = 1, + albumPerformer = performerName, + buyNowLink = deepRelease.Id.ToString(), + albumReleaseDate = releaseDate, + albumGenre = genre, + numberOfTracks = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media[0].TrackCount : 0, + IsMultiDisk = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media.Count > 1 : false, + albumCover = albumArtMbid + }); + _logger.LogInformation($"Finished building MDSR-CD result for MBID: {release.Id}"); + } }); _logger.LogInformation($"Found {resultList.Count} results"); diff --git a/Zune.Net.MetaServices/Program.cs b/Zune.Net.MetaServices/Program.cs index 565bb3f..0ce4aec 100644 --- a/Zune.Net.MetaServices/Program.cs +++ b/Zune.Net.MetaServices/Program.cs @@ -1,3 +1,5 @@ +using MetaBrainz.MusicBrainz; +using Zune.DB; using Zune.Net; using Zune.Net.Helpers; @@ -6,8 +8,10 @@ // Add services to the container. builder.Services.AddControllers().AddXmlSerializerFormatters(); -builder.Services.AddSingleton(typeof(WMIS)); -builder.Host.ConfigureZuneDB(); +builder.Host.ConfigureZuneDB(true); +builder.Services.AddTransient(); +builder.Services.AddSingleton(new Query("Zune", "4.8", "https://github.com/xerootg/ZuneNetApi")); +builder.Services.AddTransient(typeof(WMIS)); var app = builder.Build(); diff --git a/Zune.Net.Shared/Extensions.cs b/Zune.Net.Shared/Extensions.cs index d780d9f..d3c625f 100644 --- a/Zune.Net.Shared/Extensions.cs +++ b/Zune.Net.Shared/Extensions.cs @@ -30,13 +30,16 @@ public static IApplicationBuilder UseRequestBuffering(this IApplicationBuilder a }); } - public static IHostBuilder ConfigureZuneDB(this IHostBuilder host) + public static IHostBuilder ConfigureZuneDB(this IHostBuilder host, bool skipRegisterContext = false) { return host.ConfigureServices((ctx, s) => { BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String)); s.Configure(ctx.Configuration.GetSection("ZuneNetContext")); - s.AddSingleton(); + if(!skipRegisterContext) + { + s.AddSingleton(); + } }); } diff --git a/runProd.sh b/runProd.sh index 2ba123a..d4f403d 100755 --- a/runProd.sh +++ b/runProd.sh @@ -1,4 +1,6 @@ #!/bin/bash docker-compose stop docker-compose build -docker-compose start #??? --prune-orphans \ No newline at end of file +# if the images aren't getting refreshed, make sure to run docker-compose up first +docker-compose up --no-start --prune-orphans --restart unless-stopped +docker-compose start \ No newline at end of file From 3314d8adbaa6b1b0eb750c6b1a5c0978aeaea89e Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Sun, 30 Apr 2023 23:43:07 -0600 Subject: [PATCH 15/29] make runProd do important things like recreate the containers. Maybe someday, ill add the restart policy --- runProd.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runProd.sh b/runProd.sh index d4f403d..035b5e4 100755 --- a/runProd.sh +++ b/runProd.sh @@ -2,5 +2,5 @@ docker-compose stop docker-compose build # if the images aren't getting refreshed, make sure to run docker-compose up first -docker-compose up --no-start --prune-orphans --restart unless-stopped +docker-compose up -d #--no-start --prune-orphans --restart unless-stopped docker-compose start \ No newline at end of file From 05cb0a3fde929cffab30754c553ca57144fd94fb Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Tue, 2 May 2023 09:54:01 -0600 Subject: [PATCH 16/29] Handle the full Album search flow for FAI --- Zune.Net.MetaServices/Controllers/FAI.cs | 43 ++-- .../DomainModels/MDAR-CD/MdarCd.cs | 22 ++ .../MDAR-CD/MdarCdRequestMetadata.cs | 11 + .../DomainModels/MDAR-CD/MdarTrack.cs | 16 ++ .../DomainModels/MDR-CD/MdrBackoff.cs | 12 + .../DomainModels/MDR-CD/MdrCd.cs | 66 ++++++ .../DomainModels/MDR-CD/MdrRequestMetadata.cs | 18 ++ .../DomainModels/MDR-CD/MdrTrack.cs | 39 ++++ .../DomainModels/MDSR-CD/MdsrAlbum.cs | 40 ++++ .../MdsrAlbumRequestMetadata.cs} | 6 +- .../MDSR-CD/MdsrAlbumSearchResult.cs | 13 ++ .../MDSR/ExtractedFromUIX/Album.cs | 18 -- .../MDSR/ExtractedFromUIX/AlbumDetails.cs | 14 -- .../ExtractedFromUIX/AlbumDetailsTrack.cs | 10 - .../MDSR/ExtractedFromUIX/AlbumList.cs | 9 - .../MDSR/ExtractedFromUIX/Artist.cs | 16 -- .../MDSR/ExtractedFromUIX/ArtistList.cs | 10 - .../MDSR/ExtractedFromUIX/Track.cs | 22 -- .../MDSR/ExtractedFromUIX/TrackList.cs | 9 - .../DomainModels/MDSR/MDSRCD.cs | 15 -- .../DomainModels/MDSR/Result.cs | 37 ---- .../DomainModels/MDSR/SearchResult.cs | 12 - .../DomainModels/MDSR/Track.cs | 19 -- Zune.Net.MetaServices/Models/WMIS.cs | 207 +++++++++--------- Zune.Net.MetaServices/Program.cs | 4 +- docker-compose.yml | 12 + runProd.sh | 4 +- 27 files changed, 381 insertions(+), 323 deletions(-) create mode 100644 Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDR-CD/MdrBackoff.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDR-CD/MdrTrack.cs create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs rename Zune.Net.MetaServices/DomainModels/{MDSR/Metadata.cs => MDSR-CD/MdsrAlbumRequestMetadata.cs} (60%) create mode 100644 Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumSearchResult.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Album.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetails.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetailsTrack.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumList.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Artist.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/ArtistList.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Track.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/TrackList.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/MDSRCD.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/Result.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/SearchResult.cs delete mode 100644 Zune.Net.MetaServices/DomainModels/MDSR/Track.cs diff --git a/Zune.Net.MetaServices/Controllers/FAI.cs b/Zune.Net.MetaServices/Controllers/FAI.cs index e29e4eb..c9d76f8 100644 --- a/Zune.Net.MetaServices/Controllers/FAI.cs +++ b/Zune.Net.MetaServices/Controllers/FAI.cs @@ -17,46 +17,35 @@ public FAI(WMIS wmis, ILogger logger) } [HttpGet("Search")] [Produces("application/xml")] - public async Task Search(string SearchString, string resultTypeString) + public async Task Search(string SearchString, string resultTypeString, int maxNumberOfResults = 10) { // var results = await MusicBrainz.MDARSearchAlbums(SearchString); // //resultTypeString album or artist // return results[0]; switch (resultTypeString) { - case "album": - // if SearchString, not specific, but artistId is specific to artist - // return Ok(new AlbumList{ - // ReturnCode = "SUCCESS", - // Items = await MusicBrainz.SearchForAlbums(SearchString) - // }); - return Ok(await _wmis.SearchAlbumsAsync(SearchString)); - // case "artist": - // return Ok(new ArtistList(){ - // ReturnCode = "SUCCESS", - // Items = new List() - // }); - // case "track": - // return Ok(new TrackList() - // { - // ReturnCode = "SUCCESS", - // Items = new List() - // }); + case "album": //{WMISFAIAlbumsQuery}, List Album + // mdsr-cd + // UIX Type: AlbumList, of Album + return Ok(await _wmis.SearchAlbumsAsync(SearchString, maxNumberOfResults)); + case "artist": + // mdsr-cd, but with slightly different elements + // UIX Type: ArtistList, of Artist + return Ok("SUCCESS"); + case "track": + return Ok(); } return NotFound(); } - // Also known as WMISFAIGetAlbumsByArtistQuery in the UIX side + // example - http://metaservices.zune.net/ZuneFAI/GetAlbumDetailsFromAlbumId?albumId=8144290952437462496&locale=1033&volume=1 + // Also known as WMISFAIGetAlbumDetailsQuery in the UIX side + // Looking for AlbumDetails - AKA mdar-cd [HttpPost("GetAlbumDetailsFromAlbumId")] [Produces("application/xml")] - public async Task MDARGetAlbumDetailsFromAlbumId([FromBody]MdqRequestMetadata request, int albumId, int locale, int volume) + public async Task MDARGetAlbumDetailsFromAlbumId([FromBody]MdqRequestMetadata request, Int64 albumId, int locale, int volume) { - _logger.LogInformation($"MDQ-CD request for Album: {request.MdqCd.Album.AlbumTitle.Text} by {request.MdqCd.Album.AlbumArtist.Text}"); - foreach(var track in request.MdqCd.Tracks) - { - _logger.LogInformation($"Track: {track.TrackNumber} - {track.Title.Text}: RequestID: {track.trackRequestId}"); - } - return Ok(); + return Ok(await _wmis.GetMdarCdRequestFromInt64(albumId, volume)); } } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs new file mode 100644 index 0000000..a137687 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs @@ -0,0 +1,22 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdarCd +{ + public class MdarCd + { + [XmlElement("Title")] + public string Title { get; set; } + + [XmlElement("AlbumId")] + public long AlbumId { get; set; } + + [XmlElement("Volume")] + public int Volume { get; set; } + + [XmlElement("track")] + public List Items { get; set; } + + [XmlElement("ReturnCode")] + public string ReturnCode = "SUCCESS"; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs new file mode 100644 index 0000000..38bfdf7 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdarCd +{ + [XmlRoot("METADATA")] + public class MdarCdRequestMetadata + { + [XmlElement("MDAR-CD")] + public MdarCd MdarCd {get; set;} + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs new file mode 100644 index 0000000..a1a3341 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdarCd +{ + public class MdarTrack + { + [XmlElement("Title")] + public string Title { get; set; } + + [XmlElement("Performers")] + public string Performers { get; set; } + + [XmlElement("TrackNum")] + public int TrackNumber { get; set; } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrBackoff.cs b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrBackoff.cs new file mode 100644 index 0000000..0256dfa --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrBackoff.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdrCd +{ + [XmlRoot("Backoff")] + public class Backoff + { + + [XmlElement("Time")] + public int Time { get; set; } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs new file mode 100644 index 0000000..5b02ba6 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs @@ -0,0 +1,66 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdrCd +{ + [XmlRoot("MDR-CD")] + public class MDRCD + { + + [XmlElement("uniqueFileID")] + public object UniqueFileID { get; set; } + + [XmlElement("providerStyle")] + public object ProviderStyle { get; set; } + + [XmlElement("publisherRating")] + public object PublisherRating { get; set; } + + [XmlElement("buyParams")] + public object BuyParams { get; set; } + + [XmlElement("moreInfoParams")] + public object MoreInfoParams { get; set; } + + [XmlElement("dataProvider")] + public object DataProvider { get; set; } + + [XmlElement("dataProviderParams")] + public object DataProviderParams { get; set; } + + [XmlElement("dataProviderLogo")] + public object DataProviderLogo { get; set; } + + [XmlElement("version")] + public int Version { get; set; } + + [XmlElement("mdqRequestID")] + public string MdqRequestID { get; set; } + + [XmlElement("WMCollectionID")] + public string WMCollectionID { get; set; } + + [XmlElement("WMCollectionGroupID")] + public string WMCollectionGroupID { get; set; } + + [XmlElement("albumTitle")] + public string AlbumTitle { get; set; } + + [XmlElement("albumArtist")] + public int AlbumArtist { get; set; } + + [XmlElement("releaseDate")] + public DateTime ReleaseDate { get; set; } + + [XmlElement("label")] + public string Label { get; set; } + + [XmlElement("genre")] + public string Genre { get; set; } + + [XmlElement("needIDs")] + public int NeedIDs { get; set; } + + [XmlElement("track")] + public List Track { get; set; } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs new file mode 100644 index 0000000..3638487 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs @@ -0,0 +1,18 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdrCd +{ + [XmlRoot("METADATA")] + public class MdrRequestMetadata + { + + [XmlElement("MDR-CD")] + public MdrCd.MDRCD MDRCD { get; set; } + + [XmlElement("Backoff")] + public Backoff Backoff { get; set; } + + [XmlElement("mdqRequestID")] + public string MdqRequestID { get; set; } + } +} diff --git a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrTrack.cs b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrTrack.cs new file mode 100644 index 0000000..2af623d --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrTrack.cs @@ -0,0 +1,39 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdrCd +{ + [XmlRoot("track")] + public class Track + { + + [XmlElement("uniqueFileID")] + public object UniqueFileID { get; set; } + + [XmlElement("WMContentID")] + public string WMContentID { get; set; } + + [XmlElement("trackRequestID")] + public int TrackRequestID { get; set; } + + [XmlElement("trackTitle")] + public string TrackTitle { get; set; } + + [XmlElement("trackNumber")] + public int TrackNumber { get; set; } + + [XmlElement("trackPerformer")] + public int TrackPerformer { get; set; } + + [XmlElement("trackComposer")] + public object TrackComposer { get; set; } + + [XmlElement("trackConductor")] + public object TrackConductor { get; set; } + + [XmlElement("period")] + public string Period { get; set; } + + [XmlElement("explicitLyrics")] + public int ExplicitLyrics { get; set; } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs new file mode 100644 index 0000000..9c3a60b --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs @@ -0,0 +1,40 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdsrCd +{ + public class MdsrAlbum + { + [XmlElement("albumFullTitle")] + public string Title { get; set; } + + [XmlElement("id_album")] + public long Id { get; set; } + + [XmlElement("albumPerformer")] + public string AlbumArtist { get; set; } + + [XmlElement("albumGenre")] + public string Genre { get; set; } + + [XmlElement("Volume")] + public int Volume { get; set; } + + [XmlElement("albumReleaseDate")] + public DateTime ReleaseDate { get; set; } + + [XmlElement("numberOfTracks")] + public int NumberOfTracks { get; set; } + + [XmlElement("bestmatch")] + public bool BestMatch { get; set; } + + [XmlElement("IsMultiDisc")] + public bool IsMultiDisc { get; set; } + + [XmlElement("albumCover")] + public string CoverParms { get; set; } + + [XmlElement("buyNowLink")] + public string BuyNowParms { get; set; } + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/Metadata.cs b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumRequestMetadata.cs similarity index 60% rename from Zune.Net.MetaServices/DomainModels/MDSR/Metadata.cs rename to Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumRequestMetadata.cs index 07b7f49..db46eb1 100644 --- a/Zune.Net.MetaServices/DomainModels/MDSR/Metadata.cs +++ b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumRequestMetadata.cs @@ -2,13 +2,13 @@ using System.Collections.Generic; using System.Xml.Serialization; -namespace Zune.Net.MetaServices.DomainModels.MDSR +namespace Zune.Net.MetaServices.DomainModels.MdsrCd { // irritating name to make it generate correctly [XmlRoot("METADATA")] - public class MDSRCDMetadata + public class MdsrAlbumRequestMetadata { [XmlElement("MDSR-CD")] // of type (UIX) Album - public MDSRCD mDSRcD {get; set;} + public MdsrAlbumSearchResult mDSRcD {get; set;} } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumSearchResult.cs b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumSearchResult.cs new file mode 100644 index 0000000..6ca0f0c --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumSearchResult.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdsrCd +{ + public class MdsrAlbumSearchResult + { + [XmlArray("SearchResult")] + [XmlArrayItem("Result")] + public List Results; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Album.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Album.cs deleted file mode 100644 index 73d509d..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Album.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace Zune.Xml.MDAR -{ - public class Album{ - public string Title; - public Int64 Id; //not requred - public string AlbumArtist; - public string Genre; - public int Volume; - public DateTime ReleaseDate; - public int NumberOfTracks; - public Boolean BestMatch; - public Boolean IsMultiDisk; - public string CoverParams; - public string BuyNowParams; - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetails.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetails.cs deleted file mode 100644 index 497a1ab..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetails.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Zune.Xml.MDAR -{ - public class AlbumDetails - { - public string Title; - public Int64 AlbumId; - public int Volume; - public List Items; - public string ReturnCode; - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetailsTrack.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetailsTrack.cs deleted file mode 100644 index 425eca6..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumDetailsTrack.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Zune.Xml.MDAR -{ - public class AlbumDetailsTrack - { - public string Title; - public string Performers; - public int TrackNumber; - } - -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumList.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumList.cs deleted file mode 100644 index 6d7384f..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/AlbumList.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace Zune.Xml.MDAR -{ - public class AlbumList{ - public List Items; - public string ReturnCode; - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Artist.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Artist.cs deleted file mode 100644 index 345e0e5..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Artist.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace Zune.Xml.MDAR -{ - // Artist List should return a list of these - public class Artist - { - public string ArtistName; - public Int64 Id; - public string ImageParams; - public int AlbumCount; - public Boolean BestMatch; - public int TrackCount; - public string Genre; - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/ArtistList.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/ArtistList.cs deleted file mode 100644 index e968717..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/ArtistList.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Zune.Xml.MDAR -{ - public class ArtistList - { - public List Items; - public string ReturnCode; - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Track.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Track.cs deleted file mode 100644 index e9fb4b9..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/Track.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace Zune.Xml.MDAR -{ - public class Track - { - public string Title; - public Int64 TrackId; - public Int64 AlbumId; - public string Genre; - public int Volume; - public DateTime ReleaseDate; - public int NumberOfTracks; - public Boolean BestMatch; - public Boolean IsMultiDisk; - public int TrackNumber; - public string CoverParams; - public string BuyNowParams; - public string AlbumArtist; - public string AlbumTitle; - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/TrackList.cs b/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/TrackList.cs deleted file mode 100644 index 6220d74..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/ExtractedFromUIX/TrackList.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace Zune.Xml.MDAR -{ - public class TrackList{ - public List Items; - public string ReturnCode; - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/MDSRCD.cs b/Zune.Net.MetaServices/DomainModels/MDSR/MDSRCD.cs deleted file mode 100644 index caa6735..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/MDSRCD.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Serialization; - -namespace Zune.Net.MetaServices.DomainModels.MDSR -{ - public class MDSRCD - { - [XmlElement] - public string ReturnCode { get; set; } = "SUCCESS"; // default to success, we don't know what nothing looks like. I've seen rumors of an empty MDAR-CD element. - - [XmlElement] - public SearchResult SearchResult { get; set; } - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/Result.cs b/Zune.Net.MetaServices/DomainModels/MDSR/Result.cs deleted file mode 100644 index 6b6630d..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/Result.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Serialization; - -namespace Zune.Net.MetaServices.DomainModels.MDSR -{ - public class Result - { - - [XmlElement] - public bool bestmatch { get; set; } = false; - - [XmlElement] - public Int64 album_id { get; set; } = 0; - [XmlElement] - public int Volume { get; set; } = 0; - - [XmlElement] - public string albumPerformer { get; set; } = string.Empty; // Album Artist - - [XmlElement] - public string albumCover { get; set; } = string.Empty; // probably a URL. Does nothing so far - [XmlElement] - public string buyNowLink { get; set; } = string.Empty; // redirects to http://social.zune.net/album/FindAlbum.aspx?&culture=en-US - - [XmlElement] - public string albumReleaseDate { get; set; } = string.Empty; // yyyy-mm-dd - - [XmlElement] - public string albumGenre { get; set; } = string.Empty; // i.e. dance - - [XmlElement] - public bool IsMultiDisk { get; set; } = false; - [XmlElement] - public int numberOfTracks {get; set;} = 0; - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/SearchResult.cs b/Zune.Net.MetaServices/DomainModels/MDSR/SearchResult.cs deleted file mode 100644 index 468a2f4..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/SearchResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Serialization; - -namespace Zune.Net.MetaServices.DomainModels.MDSR -{ - public class SearchResult - { - [XmlElement("Result")] - public List Results; - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR/Track.cs b/Zune.Net.MetaServices/DomainModels/MDSR/Track.cs deleted file mode 100644 index 6e0f466..0000000 --- a/Zune.Net.MetaServices/DomainModels/MDSR/Track.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Zune.Net.MetaServices.DomainModels.MDSR -{ - public class MDARTrack - { - public int TrackRequestId {get; set;} = -1; // -1 in example - public Guid TrackWmid {get; set;} = new Guid(); - public string TrackNum {get; set;} = string.Empty; - public string Title {get; set;} = string.Empty; - public string Duration {get; set;} = string.Empty; // mm:ss - public string UniqueFIleId {get; set;} = string.Empty; //AMGp_id=P 3812;AMGt_id=T 4766848 - public string Performers {get; set;} = string.Empty; - public string Composers {get; set;} = string.Empty; - public string Conductors {get; set;} = string.Empty; - public string Period {get; set;} = string.Empty; //???? - public bool ExplicitLyrics = false; - } -} \ No newline at end of file diff --git a/Zune.Net.MetaServices/Models/WMIS.cs b/Zune.Net.MetaServices/Models/WMIS.cs index 9c0cbcf..7671ae8 100644 --- a/Zune.Net.MetaServices/Models/WMIS.cs +++ b/Zune.Net.MetaServices/Models/WMIS.cs @@ -1,56 +1,17 @@ using System.Collections.Concurrent; using MetaBrainz.MusicBrainz; using Zune.DB; -using Zune.Net.MetaServices.DomainModels.MDSR; +using Zune.Net.MetaServices.DomainModels.MdarCd; +using Zune.Net.MetaServices.DomainModels.MdsrCd; namespace Zune.Net.Helpers { public class WMIS { public readonly Query _query; - // public static async Task> SearchForAlbums(string query) - // { - // var results = await _query.FindReleasesAsync(query, simple: true, limit: 1); - // var releases = results.Results.Select(x => x.Item).ToList(); - // var ret = new List(); - // foreach (var album in releases) - // { - // var deepRelease = await _query.LookupReleaseAsync(album.Id, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); - // var genre = "Unknown"; - // if (deepRelease.Genres?.Count > 0) - // { - // genre = deepRelease.Genres[0].Name; - // } - // var releaseDate = DateTime.Now; - // if (deepRelease.Date != null) - // { - // releaseDate = new DateTime(deepRelease.Date.Year ?? DateTime.Now.Year, deepRelease.Date.Month ?? DateTime.Now.Month, deepRelease.Date.Day ?? DateTime.Now.Day); - // } - // try - // { - // ret.Add(new Album() - // { - // Title = deepRelease.Title, - // Id = 1, - // AlbumArtist = deepRelease.ArtistCredit[0].Name, - // Genre = genre, - // Volume = 1, - // ReleaseDate = releaseDate, - // NumberOfTracks = deepRelease.Media[0].TrackCount, - // BestMatch = (int)results.Results.Where(x => x.Item.Id == album.Id).First().Score == 100, - // IsMultiDisk = deepRelease.Media.Count > 1, - // CoverParams = string.Empty, - // BuyNowParams = string.Empty - // }); - // } - // catch { } - // } - // return ret; - // } private readonly ZuneNetContext _database; private readonly ILogger _logger; - public WMIS(Query query, ZuneNetContext database, ILogger logger) { _database = database; @@ -58,88 +19,138 @@ public WMIS(Query query, ZuneNetContext database, ILogger logger) _query = query; } - public async Task SearchAlbumsAsync(string query) + public async Task SearchAlbumsAsync(string query, int limit) { _logger.LogInformation($"Getting MDSR-CD results for AlbumSearch: {query}"); - var results = await _query.FindReleasesAsync(query, simple: true); //, limit: 2); + var results = await _query.FindReleasesAsync(query, simple: true, limit: 10); var releases = results.Results.Select(x => x.Item).ToList(); - var resultList = new ConcurrentBag(); + var resultList = new ConcurrentBag(); await Parallel.ForEachAsync(releases, async (release, ct) => { - if (ct.IsCancellationRequested) - { - return; - } - - _logger.LogInformation($"Getting all data for MBID: {release.Id}"); - var deepRelease = await _query.LookupReleaseAsync(release.Id, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); - - var label = string.Empty; - var genre = "Unknown"; - var performerName = "Unknown Artist"; - var artistGuid = Guid.Empty; - var releaseDate = DateTime.Now.ToString("o"); - var albumArtMbid = "default"; - if (deepRelease.Genres?.Count > 0) + try { - genre = deepRelease.Genres?[0]?.Name; + var albumResult = await GetMdsrAlbumByMbid(release.Id, ct, (int)results.Results.Where(x => x.Item.Id == release.Id).First().Score == 100); + if (albumResult != null) + { + resultList.Add(albumResult); + _logger.LogInformation($"Finished building MDSR-CD result for MBID: {release.Id}"); + } + _logger.LogInformation($"No MDSR-CD result for MBID: {release.Id}"); } - if (deepRelease.LabelInfo?.Count > 0) + catch (Exception e) { - label = deepRelease.LabelInfo[0].Label?.Name ?? deepRelease.LabelInfo[0].CatalogNumber; + _logger.LogError(e, $"Exception occured while processing request for MBID (album) {release.Id}"); } - if (deepRelease.ArtistCredit?.Count > 0) + }); + + _logger.LogInformation($"Found {resultList.Count} results"); + + // How's that for a stackup? + return new MdsrAlbumRequestMetadata() + { + mDSRcD = new MdsrAlbumSearchResult() { - performerName = deepRelease.ArtistCredit?[0]?.Artist.Name; - artistGuid = deepRelease.ArtistCredit[0].Artist.Id; + Results = resultList.ToList() } - if (deepRelease.Date != null && !deepRelease.Date.IsEmpty) - { + }; + } - releaseDate = deepRelease.Date.NearestDate.ToString("o"); - } - if (deepRelease.CoverArtArchive?.Front ?? false) - { - _logger.LogInformation($"Release {release.Id} HAS ARTWORK"); - albumArtMbid = release.Id.ToString(); + private async Task GetMdsrAlbumByMbid(Guid guid, CancellationToken ct, bool bestmatch = false) + { + if (ct.IsCancellationRequested) + { + return null; + } + + _logger.LogInformation($"Getting all data for MBID: {guid}"); + var deepRelease = await _query.LookupReleaseAsync(guid, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); + + if (deepRelease.Title == null) + { + return null; + } + var genre = "Unknown"; + var performerName = "Unknown Artist"; + var releaseDate = DateTime.Now; + var albumArtMbid = "default"; + if (deepRelease.Genres?.Count > 0) + { + genre = deepRelease.Genres?[0]?.Name; + } + if (deepRelease.ArtistCredit?.Count > 0) + { + performerName = deepRelease.ArtistCredit?[0]?.Artist.Name; + } + if (deepRelease.Date != null && !deepRelease.Date.IsEmpty) + { + + releaseDate = deepRelease.Date.NearestDate; + } + if (deepRelease.CoverArtArchive?.Front ?? false) + { + _logger.LogInformation($"Release {guid} HAS ARTWORK"); + albumArtMbid = guid.ToString(); + } + + var recordId = await _database.CreateOrGetAlbumIdInt64Async(guid); - var recordId = await _database.CreateOrGetAlbumIdInt64Async(release.Id); + return new MdsrAlbum() + { + Title = deepRelease.Title, + BestMatch = bestmatch, + Id = recordId, + Volume = 1, + AlbumArtist = performerName, + BuyNowParms = deepRelease.Id.ToString(), + ReleaseDate = releaseDate, + Genre = genre, + NumberOfTracks = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media[0].TrackCount : 0, + IsMultiDisc = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media.Count > 1 : false, + CoverParms = albumArtMbid + }; + } + + public async Task GetMdarCdRequestFromInt64(Int64 albumId, int volume) + { + var mbid = await _database.GetAlbumIdRecordAsync(albumId); + if (!mbid.HasValue) + { + throw new KeyNotFoundException($"Cannot locate a MBID for {albumId}, please start the FAI request over"); + } + var deepRelease = await _query.LookupReleaseAsync(mbid.Value, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); - resultList.Add(new Result() + var tracks = new List(); + if (deepRelease.Media != null && deepRelease.Media.Count > 0) + { + foreach (var track in deepRelease.Media[0].Tracks) + { + var trackTitle = track.Title ?? "Unknown Title"; + var trackNumber = int.Parse(track.Number ?? "0"); + var trackArtist = track.ArtistCredit?[0]?.Name ?? deepRelease.ArtistCredit?[0]?.Name ?? "Unknown Artist"; + + tracks.Add(new MdarTrack() { - bestmatch = (int)results.Results.Where(x => x.Item.Id == deepRelease.Id).First().Score == 100, - album_id = recordId, - Volume = 1, - albumPerformer = performerName, - buyNowLink = deepRelease.Id.ToString(), - albumReleaseDate = releaseDate, - albumGenre = genre, - numberOfTracks = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media[0].TrackCount : 0, - IsMultiDisk = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media.Count > 1 : false, - albumCover = albumArtMbid + Title = trackTitle, + Performers = trackArtist, + TrackNumber = trackNumber }); - _logger.LogInformation($"Finished building MDSR-CD result for MBID: {release.Id}"); } - }); - - _logger.LogInformation($"Found {resultList.Count} results"); + } - // How's that for a stackup? - var ret = new MDSRCDMetadata() + return new MdarCdRequestMetadata() { - mDSRcD = new MDSRCD() + MdarCd = new MdarCd() { - SearchResult = new SearchResult() - { - Results = resultList.ToList() - } + Title = deepRelease.Title, + AlbumId = albumId, + Volume = volume, + Items = tracks, + } }; - - return ret; } } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/Program.cs b/Zune.Net.MetaServices/Program.cs index 0ce4aec..79081f1 100644 --- a/Zune.Net.MetaServices/Program.cs +++ b/Zune.Net.MetaServices/Program.cs @@ -8,13 +8,15 @@ // Add services to the container. builder.Services.AddControllers().AddXmlSerializerFormatters(); +builder.Services.AddResponseCaching(); builder.Host.ConfigureZuneDB(true); builder.Services.AddTransient(); -builder.Services.AddSingleton(new Query("Zune", "4.8", "https://github.com/xerootg/ZuneNetApi")); +builder.Services.AddSingleton(new Query("Zune", "4.8", "https://github.com/xerootg/ZuneNetApi")); builder.Services.AddTransient(typeof(WMIS)); var app = builder.Build(); app.MapControllers(); +app.UseResponseCaching(); app.Run(); diff --git a/docker-compose.yml b/docker-compose.yml index 2465c9c..9b018c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,7 @@ networks: services: mongodb: + restart: unless-stopped image: mongo:latest environment: MONGO_INITDB_ROOT_USERNAME: root @@ -22,6 +23,7 @@ services: - zune.net nginx: + restart: unless-stopped build: context: nginx ports: @@ -32,6 +34,7 @@ services: - zune.net catalog: + restart: unless-stopped build: context: ./ dockerfile: Zune.Net.Catalog/Dockerfile @@ -42,6 +45,7 @@ services: - backend catalog.image: + restart: unless-stopped build: context: ./ dockerfile: Zune.Net.Catalog.Image/Dockerfile @@ -52,6 +56,7 @@ services: - backend commerce: + restart: unless-stopped build: context: ./ dockerfile: Zune.Net.Commerce/Dockerfile @@ -62,6 +67,7 @@ services: - backend inbox: + restart: unless-stopped build: context: ./ dockerfile: Zune.Net.Inbox/Dockerfile @@ -72,6 +78,7 @@ services: - backend login: + restart: unless-stopped build: context: ./ dockerfile: Zune.Net.Login/Dockerfile @@ -82,6 +89,7 @@ services: - backend metaservices: + restart: unless-stopped build: context: ./ dockerfile: Zune.Net.MetaServices/Dockerfile @@ -92,6 +100,7 @@ services: - backend mix: + restart: unless-stopped build: context: ./ dockerfile: Zune.Net.Mix/Dockerfile @@ -102,6 +111,7 @@ services: - backend social: + restart: unless-stopped build: context: ./ dockerfile: Zune.Net.SocialApi/Dockerfile @@ -112,6 +122,7 @@ services: - backend tiles: + restart: unless-stopped build: context: ./ dockerfile: Zune.Net.Tiles/Dockerfile @@ -122,6 +133,7 @@ services: - backend tuners: + restart: unless-stopped build: context: ./ dockerfile: Zune.Net.Tuners/Dockerfile diff --git a/runProd.sh b/runProd.sh index 035b5e4..01c3490 100755 --- a/runProd.sh +++ b/runProd.sh @@ -1,6 +1,4 @@ #!/bin/bash docker-compose stop docker-compose build -# if the images aren't getting refreshed, make sure to run docker-compose up first -docker-compose up -d #--no-start --prune-orphans --restart unless-stopped -docker-compose start \ No newline at end of file +docker-compose up -d --remove-orphans From 7a56ac559cdefe3e2eac64bf3e633fa2306ad959 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Wed, 3 May 2023 19:08:55 -0600 Subject: [PATCH 17/29] More WMIS/FAI stuff --- Zune.DB/Models/WMISAlbumIdEntry.cs | 6 +- Zune.DB/Models/WMISAlbumTrackEntry.cs | 25 +++ Zune.DB/ZuneNetContext.cs | 39 +++- Zune.DB/ZuneNetContextSettings.cs | 2 + Zune.Net.MetaServices/Controllers/FAI.cs | 16 +- Zune.Net.MetaServices/Controllers/MDRCD.cs | 38 ++++ Zune.Net.MetaServices/Controllers/redir.cs | 14 +- .../DomainModels/MDAR-CD/MdarCd.cs | 6 + .../MDAR-CD/MdarCdRequestMetadata.cs | 3 + .../DomainModels/MDAR-CD/MdarTrack.cs | 3 + .../DomainModels/MDR-CD/MdrCd.cs | 30 +-- .../DomainModels/MDR-CD/MdrRequestMetadata.cs | 2 +- .../DomainModels/MDR-CD/MdrTrack.cs | 18 +- Zune.Net.MetaServices/Models/WMIS.cs | 174 +++++++++++++++--- Zune.Net.MetaServices/Program.cs | 4 + .../Zune.Net.MetaServices.csproj | 4 + 16 files changed, 323 insertions(+), 61 deletions(-) create mode 100644 Zune.DB/Models/WMISAlbumTrackEntry.cs create mode 100644 Zune.Net.MetaServices/Controllers/MDRCD.cs diff --git a/Zune.DB/Models/WMISAlbumIdEntry.cs b/Zune.DB/Models/WMISAlbumIdEntry.cs index 6e26b1d..c90b3d9 100644 --- a/Zune.DB/Models/WMISAlbumIdEntry.cs +++ b/Zune.DB/Models/WMISAlbumIdEntry.cs @@ -8,8 +8,10 @@ public class WMISAlbumIdEntry { [BsonId] public string Id; - public Int64 AlbumId {get; set;} - public Guid AlbumGuid {get; set;} + + public long AlbumId { get; set; } + + public Guid AlbumGuid { get; set; } public WMISAlbumIdEntry(Int64 id, Guid guid) { diff --git a/Zune.DB/Models/WMISAlbumTrackEntry.cs b/Zune.DB/Models/WMISAlbumTrackEntry.cs new file mode 100644 index 0000000..05d5a0c --- /dev/null +++ b/Zune.DB/Models/WMISAlbumTrackEntry.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using MongoDB.Bson.Serialization.Attributes; + +namespace Zune.DB.Models +{ + public class WMISAlbumTrackEntry + { + [BsonId] + public string RecordId { get; set; } + public Guid AlbumMbid { get; set; } // musicbrainz id, so we can fetch the thing later + public Guid TrackMbid { get; set; } + public int TrackId { get; set; } // this is the ID that is used by the WMIS document + public int TrackDuration { get; set; } + + public WMISAlbumTrackEntry(int trackId, int trackDuration, Guid albumMbid, Guid trackMbid) + { + RecordId = (trackId + trackDuration).ToString(); + TrackId = trackId; + TrackDuration = trackDuration; + AlbumMbid = albumMbid; + TrackMbid = trackMbid; + } + } +} \ No newline at end of file diff --git a/Zune.DB/ZuneNetContext.cs b/Zune.DB/ZuneNetContext.cs index 897f43d..5ef2a52 100644 --- a/Zune.DB/ZuneNetContext.cs +++ b/Zune.DB/ZuneNetContext.cs @@ -15,6 +15,7 @@ public class ZuneNetContext private readonly IMongoCollection _authCollection; private readonly IMongoCollection _imageCollection; private readonly IMongoCollection _albumLookupCollection; + private readonly IMongoCollection _trackLookupCollection; public ZuneNetContext(IOptions dbSettings) : this(dbSettings.Value) { @@ -30,6 +31,7 @@ public ZuneNetContext(ZuneNetContextSettings dbSettings) _authCollection = mongoDatabase.GetCollection(dbSettings.AuthCollectionName); _imageCollection = mongoDatabase.GetCollection(dbSettings.ImageCollectionName); _albumLookupCollection = mongoDatabase.GetCollection(dbSettings.AlbumLookupCollectionName); + _trackLookupCollection = mongoDatabase.GetCollection(dbSettings.TrackLookupCollectionName); } public async Task> GetAsync(Expression> filter = null) => @@ -126,7 +128,8 @@ public async Task AddImageAsync(string url) var existing = await _albumLookupCollection.FindAsync(x => x.AlbumId == id); var record = await existing.SingleAsync(); return record.AlbumGuid; - } catch + } + catch { return null; } @@ -139,7 +142,8 @@ public async Task AddImageAsync(string url) var existing = await _albumLookupCollection.FindAsync(x => x.AlbumGuid == id); var record = await existing.SingleAsync(); return record.AlbumId; - } catch + } + catch { return null; } @@ -164,6 +168,37 @@ public async Task CreateOrGetAlbumIdInt64Async(Guid guid) } } } + + public async Task GetTrackMbidFromTrackIdAndDurationAsync(int trackNumber, int trackDuration) + { + try + { + var record = await _trackLookupCollection.FindAsync(x => x.TrackId == trackNumber && x.TrackDuration == trackDuration); + var first = await record.FirstOrDefaultAsync(); + return first.TrackMbid; + } + catch { return null; } + } + + public async Task GetAlbumMbidFromTrackIdAndDurationAsync(int trackNumber, int trackDuration) + { + try + { + var record = await _trackLookupCollection.FindAsync(x => x.TrackId == trackNumber && x.TrackDuration == trackDuration); + var first = await record.FirstOrDefaultAsync(); + return first.AlbumMbid; + } + catch { return null; } + } + + public async Task CreateTrackReverseLookupRecordAsync(Guid albumMbid, Guid trackMbid, int trackNumber, int trackDuration) + { + if (await GetAlbumMbidFromTrackIdAndDurationAsync(trackNumber, trackDuration) == null) + { + await _trackLookupCollection.InsertOneAsync( + new WMISAlbumTrackEntry(trackNumber, trackDuration, albumMbid, trackMbid)); + } + } } } diff --git a/Zune.DB/ZuneNetContextSettings.cs b/Zune.DB/ZuneNetContextSettings.cs index 9e256f5..fa9b2ad 100644 --- a/Zune.DB/ZuneNetContextSettings.cs +++ b/Zune.DB/ZuneNetContextSettings.cs @@ -13,5 +13,7 @@ public class ZuneNetContextSettings public string ImageCollectionName { get; set; } = "Images"; public string AlbumLookupCollectionName { get; set; } = "AlbumLookup"; + + public string TrackLookupCollectionName { get; set; } = "TrackLookup"; } } diff --git a/Zune.Net.MetaServices/Controllers/FAI.cs b/Zune.Net.MetaServices/Controllers/FAI.cs index c9d76f8..3b6c30a 100644 --- a/Zune.Net.MetaServices/Controllers/FAI.cs +++ b/Zune.Net.MetaServices/Controllers/FAI.cs @@ -42,10 +42,22 @@ public async Task Search(string SearchString, string resultTypeStr // Also known as WMISFAIGetAlbumDetailsQuery in the UIX side // Looking for AlbumDetails - AKA mdar-cd [HttpPost("GetAlbumDetailsFromAlbumId")] + [HttpGet("GetAlbumDetailsFromAlbumId")] [Produces("application/xml")] - public async Task MDARGetAlbumDetailsFromAlbumId([FromBody]MdqRequestMetadata request, Int64 albumId, int locale, int volume) + public async Task MDARGetAlbumDetailsFromAlbumId(Int64 albumId, int locale, string volume, [FromBody]MdqRequestMetadata request = null) { - return Ok(await _wmis.GetMdarCdRequestFromInt64(albumId, volume)); + var response = await _wmis.GetMdarCdRequestFromInt64(albumId, volume, request); + if(request?.MdqCd?.MdqRequestId != null) + { + response.mdqRequestID = new Guid(request?.MdqCd?.MdqRequestId); + } + return Ok(response); + } + + [HttpPost("SubmitAddFeedback")] + public IActionResult SubmitAddFeedback() + { + return Ok(); } } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/Controllers/MDRCD.cs b/Zune.Net.MetaServices/Controllers/MDRCD.cs new file mode 100644 index 0000000..26f044b --- /dev/null +++ b/Zune.Net.MetaServices/Controllers/MDRCD.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Mvc; +using Zune.Net.Helpers; +using Zune.Net.MetaServices.DomainModels.MdqCd; + +namespace Zune.Net.MetaServices.Controllers +{ + [Route("MDRCD")] + public class MDRCD : Controller + { + + private readonly WMIS _wmis; + private readonly ILogger _logger; + public MDRCD(WMIS wmis, ILogger logger) + { + _wmis = wmis; + _logger = logger; + } + + [HttpPost("mdrcdposturlbackgroundzune")] + [Produces("application/xml")] + public async Task MdrCdBackground(Guid requestID, [FromBody]MdqRequestMetadata request) + { + if(request.MdqCd.Tracks.Count != 1) + { + return BadRequest($"Got {request.MdqCd.Tracks.Count} tracks, expected 1"); + } + return Ok(await _wmis.GetMdrCdRequestFromTrackIdAsync(request.MdqCd.Tracks[0].trackRequestId, request.MdqCd.Tracks[0].TrackDurationMs, requestID)); + } + + [HttpGet("{type}")] + [HttpPost("{type}")] + public IActionResult Handle(string type) + { + return Ok(type); + } + + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/Controllers/redir.cs b/Zune.Net.MetaServices/Controllers/redir.cs index fdab813..1bd7ed6 100644 --- a/Zune.Net.MetaServices/Controllers/redir.cs +++ b/Zune.Net.MetaServices/Controllers/redir.cs @@ -5,18 +5,24 @@ namespace Zune.Net.MetaServices.Controllers { [ApiController] [Route("/redir/")] - [Produces("application/xml")] + public class Redir : ControllerBase{ + [HttpGet("ZuneFAI/")] + [Produces("application/xml")] public IActionResult ZuneFai() { return Ok(new Metadata()); } - [HttpGet("getmdrcdposturlbackgroundzune")] - public IActionResult mdrcdposturl() + //eg: mdrcdposturlbackgroundzune + [HttpGet("get{mdrcd}")] + [Produces("text/plain")] + public string MDRCDdir(string mdrcd) { - return Ok(new Metadata()); + // ZuneNativeLib does this request. It takes this string and seemingly blindly appends &requestId. + // the rest of the string is pretty useless but i don't know enough about it yet. + return $"http://metaservices.zune.net/MDRCD/{mdrcd}{HttpContext.Request.QueryString.ToUriComponent()}"; } } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs index a137687..17bc653 100644 --- a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs @@ -18,5 +18,11 @@ public class MdarCd [XmlElement("ReturnCode")] public string ReturnCode = "SUCCESS"; + + [XmlElement("WmCollectionId")] + public Guid AlbumMBID {get; set;} + + [XmlElement("WmCollectiongroupId")] + public Guid AlbumGroupMBID {get; set;} } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs index 38bfdf7..f1320dd 100644 --- a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs @@ -5,6 +5,9 @@ namespace Zune.Net.MetaServices.DomainModels.MdarCd [XmlRoot("METADATA")] public class MdarCdRequestMetadata { + [XmlElement("mdqRequestID")] + public Guid mdqRequestID{get; set;} + [XmlElement("MDAR-CD")] public MdarCd MdarCd {get; set;} } diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs index a1a3341..01baca4 100644 --- a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs @@ -12,5 +12,8 @@ public class MdarTrack [XmlElement("TrackNum")] public int TrackNumber { get; set; } + + [XmlElement("TrackWmid")] + public Guid TrackWmid {get; set;} } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs index 5b02ba6..2566346 100644 --- a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs +++ b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs @@ -7,46 +7,46 @@ public class MDRCD { [XmlElement("uniqueFileID")] - public object UniqueFileID { get; set; } + public string UniqueFileID { get; set; } [XmlElement("providerStyle")] - public object ProviderStyle { get; set; } + public string ProviderStyle { get; set; } [XmlElement("publisherRating")] - public object PublisherRating { get; set; } + public string PublisherRating { get; set; } [XmlElement("buyParams")] - public object BuyParams { get; set; } + public string BuyParams { get; set; } [XmlElement("moreInfoParams")] - public object MoreInfoParams { get; set; } + public string MoreInfoParams { get; set; } [XmlElement("dataProvider")] - public object DataProvider { get; set; } + public string DataProvider { get; set; } [XmlElement("dataProviderParams")] - public object DataProviderParams { get; set; } + public string DataProviderParams { get; set; } [XmlElement("dataProviderLogo")] - public object DataProviderLogo { get; set; } + public string DataProviderLogo { get; set; } [XmlElement("version")] public int Version { get; set; } [XmlElement("mdqRequestID")] - public string MdqRequestID { get; set; } + public Guid MdqRequestID { get; set; } [XmlElement("WMCollectionID")] - public string WMCollectionID { get; set; } + public Guid WMCollectionID { get; set; } [XmlElement("WMCollectionGroupID")] - public string WMCollectionGroupID { get; set; } + public Guid WMCollectionGroupID { get; set; } [XmlElement("albumTitle")] public string AlbumTitle { get; set; } [XmlElement("albumArtist")] - public int AlbumArtist { get; set; } + public string AlbumArtist { get; set; } [XmlElement("releaseDate")] public DateTime ReleaseDate { get; set; } @@ -60,6 +60,12 @@ public class MDRCD [XmlElement("needIDs")] public int NeedIDs { get; set; } + [XmlElement("largeCoverParams")] + public string LargeCoverAddress { get; set; } + + [XmlElement("smallCoverParams")] + public string SmallCoverAddress { get; set; } + [XmlElement("track")] public List Track { get; set; } } diff --git a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs index 3638487..3b881f1 100644 --- a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs +++ b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs @@ -13,6 +13,6 @@ public class MdrRequestMetadata public Backoff Backoff { get; set; } [XmlElement("mdqRequestID")] - public string MdqRequestID { get; set; } + public Guid MdqRequestID { get; set; } } } diff --git a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrTrack.cs b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrTrack.cs index 2af623d..2f6efce 100644 --- a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrTrack.cs +++ b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrTrack.cs @@ -7,33 +7,33 @@ public class Track { [XmlElement("uniqueFileID")] - public object UniqueFileID { get; set; } + public string UniqueFileID { get; set; } = string.Empty; [XmlElement("WMContentID")] - public string WMContentID { get; set; } + public string WMContentID { get; set; } = string.Empty; [XmlElement("trackRequestID")] public int TrackRequestID { get; set; } [XmlElement("trackTitle")] - public string TrackTitle { get; set; } + public string TrackTitle { get; set; } = string.Empty; [XmlElement("trackNumber")] - public int TrackNumber { get; set; } + public string TrackNumber { get; set; } = string.Empty; [XmlElement("trackPerformer")] - public int TrackPerformer { get; set; } + public string TrackPerformer { get; set; } = string.Empty; [XmlElement("trackComposer")] - public object TrackComposer { get; set; } + public string TrackComposer { get; set; } = string.Empty; [XmlElement("trackConductor")] - public object TrackConductor { get; set; } + public string TrackConductor { get; set; } = string.Empty; [XmlElement("period")] - public string Period { get; set; } + public string Period { get; set; } = string.Empty; [XmlElement("explicitLyrics")] - public int ExplicitLyrics { get; set; } + public int ExplicitLyrics { get; set; } = 0; } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/Models/WMIS.cs b/Zune.Net.MetaServices/Models/WMIS.cs index 7671ae8..4895e89 100644 --- a/Zune.Net.MetaServices/Models/WMIS.cs +++ b/Zune.Net.MetaServices/Models/WMIS.cs @@ -1,12 +1,16 @@ using System.Collections.Concurrent; +using System.Text.RegularExpressions; using MetaBrainz.MusicBrainz; +using MetaBrainz.MusicBrainz.Interfaces.Entities; using Zune.DB; using Zune.Net.MetaServices.DomainModels.MdarCd; +using Zune.Net.MetaServices.DomainModels.MdqCd; +using Zune.Net.MetaServices.DomainModels.MdrCd; using Zune.Net.MetaServices.DomainModels.MdsrCd; namespace Zune.Net.Helpers { - public class WMIS + public partial class WMIS { public readonly Query _query; private readonly ZuneNetContext _database; @@ -31,7 +35,7 @@ await Parallel.ForEachAsync(releases, async (release, ct) => { try { - var albumResult = await GetMdsrAlbumByMbid(release.Id, ct, (int)results.Results.Where(x => x.Item.Id == release.Id).First().Score == 100); + var albumResult = await GetMdsrAlbumByMbid(release.Id, ct, results.Results.Where(x => x.Item.Id == release.Id).First().Score == 100); if (albumResult != null) { resultList.Add(albumResult); @@ -65,9 +69,9 @@ await Parallel.ForEachAsync(releases, async (release, ct) => } _logger.LogInformation($"Getting all data for MBID: {guid}"); - var deepRelease = await _query.LookupReleaseAsync(guid, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); + var release = await _query.LookupReleaseAsync(guid, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); - if (deepRelease.Title == null) + if (release.Title == null) { return null; } @@ -76,20 +80,20 @@ await Parallel.ForEachAsync(releases, async (release, ct) => var performerName = "Unknown Artist"; var releaseDate = DateTime.Now; var albumArtMbid = "default"; - if (deepRelease.Genres?.Count > 0) + if (release.Genres?.Count > 0) { - genre = deepRelease.Genres?[0]?.Name; + genre = release.Genres?[0]?.Name; } - if (deepRelease.ArtistCredit?.Count > 0) + if (release.ArtistCredit?.Count > 0) { - performerName = deepRelease.ArtistCredit?[0]?.Artist.Name; + performerName = release.ArtistCredit?[0]?.Artist.Name; } - if (deepRelease.Date != null && !deepRelease.Date.IsEmpty) + if (release.Date != null && !release.Date.IsEmpty) { - releaseDate = deepRelease.Date.NearestDate; + releaseDate = release.Date.NearestDate; } - if (deepRelease.CoverArtArchive?.Front ?? false) + if (release.CoverArtArchive?.Front ?? false) { _logger.LogInformation($"Release {guid} HAS ARTWORK"); albumArtMbid = guid.ToString(); @@ -97,60 +101,172 @@ await Parallel.ForEachAsync(releases, async (release, ct) => var recordId = await _database.CreateOrGetAlbumIdInt64Async(guid); + var numberOfTracks = 0; + var isMultiDisc = false; + if (release.Media != null && release.Media.Count > 0) + { + numberOfTracks = release.Media[0].TrackCount; + isMultiDisc = release.Media.Count > 1; + + await Parallel.ForEachAsync(release.Media[0].Tracks, async (track, ct) => + { + // Add the tracks here + }); + } + return new MdsrAlbum() { - Title = deepRelease.Title, + Title = release.Title, BestMatch = bestmatch, Id = recordId, Volume = 1, AlbumArtist = performerName, - BuyNowParms = deepRelease.Id.ToString(), + BuyNowParms = release.Id.ToString(), ReleaseDate = releaseDate, Genre = genre, - NumberOfTracks = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media[0].TrackCount : 0, - IsMultiDisc = deepRelease.Media != null && deepRelease.Media.Count > 0 ? deepRelease.Media.Count > 1 : false, + NumberOfTracks = numberOfTracks, + IsMultiDisc = isMultiDisc, CoverParms = albumArtMbid }; } - public async Task GetMdarCdRequestFromInt64(Int64 albumId, int volume) + private async Task> GetTracksFromIReleaseAsync(IRelease release, MdqRequestMetadata requestMetadata = null) { - var mbid = await _database.GetAlbumIdRecordAsync(albumId); - if (!mbid.HasValue) - { - throw new KeyNotFoundException($"Cannot locate a MBID for {albumId}, please start the FAI request over"); - } - var deepRelease = await _query.LookupReleaseAsync(mbid.Value, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); - var tracks = new List(); - if (deepRelease.Media != null && deepRelease.Media.Count > 0) + if (release.Media != null && release.Media.Count > 0) { - foreach (var track in deepRelease.Media[0].Tracks) + foreach (var track in release.Media[0].Tracks) { var trackTitle = track.Title ?? "Unknown Title"; var trackNumber = int.Parse(track.Number ?? "0"); - var trackArtist = track.ArtistCredit?[0]?.Name ?? deepRelease.ArtistCredit?[0]?.Name ?? "Unknown Artist"; + var trackArtist = track.ArtistCredit?[0]?.Name ?? release.ArtistCredit?[0]?.Name ?? "Unknown Artist"; + var trackMbid = track.Id; + + if (requestMetadata != null) + { + // attempt to bind a trackid to a trackmbid + var thisTrackIntId = requestMetadata.MdqCd.Tracks.Where(x => x.TrackNumber == trackNumber).ToArray(); + + if (thisTrackIntId != null && thisTrackIntId.Any()) + { + await _database.CreateTrackReverseLookupRecordAsync(release.Id, trackMbid, thisTrackIntId[0].trackRequestId, thisTrackIntId[0].TrackDurationMs); + } + } tracks.Add(new MdarTrack() { Title = trackTitle, Performers = trackArtist, - TrackNumber = trackNumber + TrackNumber = trackNumber, + TrackWmid = trackMbid }); } } + return tracks; + } + + public async Task GetMdarCdRequestFromInt64(long albumId, string volume, MdqRequestMetadata requestMetadata) + { + var mbid = await _database.GetAlbumIdRecordAsync(albumId); + if (!mbid.HasValue) + { + throw new KeyNotFoundException($"Cannot locate a MBID for {albumId}, please start the FAI request over"); + } + var release = await _query.LookupReleaseAsync(mbid.Value, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); + + var tracks = await GetTracksFromIReleaseAsync(release, requestMetadata); + + var intVolume = 1; + if (int.TryParse(GetFirstInt().Match(volume).Value, out var tryVolume)) + { + intVolume = tryVolume; + } return new MdarCdRequestMetadata() { + mdqRequestID = release.Id, MdarCd = new MdarCd() { - Title = deepRelease.Title, + Title = release.Title, AlbumId = albumId, - Volume = volume, + Volume = intVolume, Items = tracks, + AlbumGroupMBID = release.Id, + AlbumMBID = release.Id + } + }; + } + + public async Task GetMdrCdRequestFromTrackIdAsync(int TrackRequestID, int trackDuration, Guid requestId) + { + var albumMbid = await _database.GetAlbumMbidFromTrackIdAndDurationAsync(TrackRequestID, trackDuration); + var trackMbid = await _database.GetTrackMbidFromTrackIdAndDurationAsync(TrackRequestID, trackDuration); + + var release = await _query.LookupReleaseAsync(albumMbid.Value, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); + var track = release.Media[0].Tracks.Where(x => x.Id == trackMbid).ToList()[0]; + + var performerName = string.Empty; + if (release.ArtistCredit?.Count > 0) + { + performerName = release.ArtistCredit?[0]?.Artist.Name; + } + + var trackPerformer = string.Empty; + if (track.ArtistCredit?.Count > 0) + { + trackPerformer = track.ArtistCredit?[0]?.Artist.Name; + } + var period = string.Empty; + + if (release.Date != null && !release.Date.IsEmpty) + { + var yearString = release.Date.Year.Value.ToString(); + var decadeString = yearString[..^1]; + period = $"{decadeString}0's"; + } + + var genre = string.Empty; + if (release.Genres?.Count > 0) + { + genre = release.Genres?[0]?.Name; + } + + return new MdrRequestMetadata() + { + MdqRequestID = requestId, + Backoff = new Backoff() + { + Time = 0, + }, + MDRCD = new MDRCD() + { + MdqRequestID = requestId, + WMCollectionGroupID = release.Id, + WMCollectionID = release.Id, + Track = new List(){new Track() + { + UniqueFileID = trackMbid.Value.ToString("N"), + TrackRequestID = TrackRequestID, + WMContentID = trackMbid.Value.ToString(), + TrackTitle = track.Title, + TrackNumber = track.Number, + TrackPerformer = trackPerformer, + Period = period + }}, + UniqueFileID = albumMbid.Value.ToString("N"), + Version = 1, + AlbumTitle = release.Title, + AlbumArtist = performerName, + ReleaseDate = release.Date.NearestDate, + Genre = genre, + LargeCoverAddress = release.Id.ToString(), + SmallCoverAddress = release.Id.ToString() } }; } + + [GeneratedRegex("\\d+")] + private static partial Regex GetFirstInt(); } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/Program.cs b/Zune.Net.MetaServices/Program.cs index 79081f1..5ec199e 100644 --- a/Zune.Net.MetaServices/Program.cs +++ b/Zune.Net.MetaServices/Program.cs @@ -8,6 +8,7 @@ // Add services to the container. builder.Services.AddControllers().AddXmlSerializerFormatters(); +builder.Services.AddSwaggerDocument(); builder.Services.AddResponseCaching(); builder.Host.ConfigureZuneDB(true); builder.Services.AddTransient(); @@ -19,4 +20,7 @@ app.MapControllers(); app.UseResponseCaching(); +app.UseOpenApi(); +app.UseSwaggerUi3(); + app.Run(); diff --git a/Zune.Net.MetaServices/Zune.Net.MetaServices.csproj b/Zune.Net.MetaServices/Zune.Net.MetaServices.csproj index 7595f91..b3ed1f3 100644 --- a/Zune.Net.MetaServices/Zune.Net.MetaServices.csproj +++ b/Zune.Net.MetaServices/Zune.Net.MetaServices.csproj @@ -11,4 +11,8 @@ + + + + From 0930bfdecafff7fd1a6d26f48a94571cd00215f3 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Thu, 4 May 2023 20:43:47 -0600 Subject: [PATCH 18/29] Add automatic track linking, introduce amg-like IDs --- Zune.DB/ZuneNetContext.cs | 26 +++- .../DomainModels/MDAR-CD/MdarBackoff.cs | 11 ++ .../DomainModels/MDAR-CD/MdarCd.cs | 56 ++++++++- .../MDAR-CD/MdarCdRequestMetadata.cs | 3 + .../DomainModels/MDAR-CD/MdarTrack.cs | 3 + .../DomainModels/MDQ-CD/MdqTrack.cs | 4 +- .../DomainModels/MDR-CD/MdrCd.cs | 37 +++--- .../DomainModels/MDR-CD/MdrRequestMetadata.cs | 4 +- Zune.Net.MetaServices/Models/WMIS.cs | 119 +++++++++++++++--- 9 files changed, 224 insertions(+), 39 deletions(-) create mode 100644 Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarBackoff.cs diff --git a/Zune.DB/ZuneNetContext.cs b/Zune.DB/ZuneNetContext.cs index 5ef2a52..aedb8fb 100644 --- a/Zune.DB/ZuneNetContext.cs +++ b/Zune.DB/ZuneNetContext.cs @@ -64,6 +64,12 @@ public async Task RemoveAsync(Guid id) => public Task ClearMembersAsync() => _memberCollection.DeleteManyAsync(_ => true); + public async Task ClearAlbumLookupAsync() + { + await _albumLookupCollection.DeleteManyAsync(_ => true); + await _trackLookupCollection.DeleteManyAsync(_ => true); + } + public async Task GetCidByToken(string token) { string tokenHash = Helpers.Hash(token); @@ -121,7 +127,7 @@ public async Task AddImageAsync(string url) public Task ClearImagesAsync() => _imageCollection.DeleteManyAsync(_ => true); - public async Task GetAlbumIdRecordAsync(Int64 id) + public async Task GetAlbumIdRecordAsync(long id) { try { @@ -135,7 +141,7 @@ public async Task AddImageAsync(string url) } } - public async Task GetAlbumIdRecordAsync(Guid id) + public async Task GetAlbumIdRecordAsync(Guid id) { try { @@ -150,7 +156,7 @@ public async Task AddImageAsync(string url) } // It's ugly, but it maps a MBID to an Int64 for WMIS's crazy lookup - public async Task CreateOrGetAlbumIdInt64Async(Guid guid) + public async Task CreateOrGetAlbumIdInt64Async(Guid guid) { var existing = await GetAlbumIdRecordAsync(guid); if (existing.HasValue) @@ -159,7 +165,8 @@ public async Task CreateOrGetAlbumIdInt64Async(Guid guid) } while (true) { - var id = new Random().NextInt64(); + var id = new Random().NextInt64(99_999_999); // Max of 8 digits for... reasons. + var found = await GetAlbumIdRecordAsync(id); if (!found.HasValue) { @@ -191,6 +198,17 @@ public async Task CreateOrGetAlbumIdInt64Async(Guid guid) catch { return null; } } + public async Task GetAlbumIDFromTrackIdAndDurationAsync(int trackNumber, int trackDuration) + { + try + { + var record = await _trackLookupCollection.FindAsync(x => x.TrackId == trackNumber && x.TrackDuration == trackDuration); + var first = await record.FirstOrDefaultAsync(); + return await CreateOrGetAlbumIdInt64Async(first.AlbumMbid); + } + catch { return null; } + } + public async Task CreateTrackReverseLookupRecordAsync(Guid albumMbid, Guid trackMbid, int trackNumber, int trackDuration) { if (await GetAlbumMbidFromTrackIdAndDurationAsync(trackNumber, trackDuration) == null) diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarBackoff.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarBackoff.cs new file mode 100644 index 0000000..c130854 --- /dev/null +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarBackoff.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace Zune.Net.MetaServices.DomainModels.MdarCd +{ + public class MdarBackoff + { + + [XmlElement("Time")] + public int Time { get; set; } = 0; + } +} \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs index 17bc653..9bf1f04 100644 --- a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs @@ -4,6 +4,9 @@ namespace Zune.Net.MetaServices.DomainModels.MdarCd { public class MdarCd { + [XmlElement("A_id")] + public string AId {get; set;} + [XmlElement("Title")] public string Title { get; set; } @@ -20,9 +23,58 @@ public class MdarCd public string ReturnCode = "SUCCESS"; [XmlElement("WmCollectionId")] - public Guid AlbumMBID {get; set;} + public Guid AlbumMBID { get; set; } [XmlElement("WmCollectiongroupId")] - public Guid AlbumGroupMBID {get; set;} + public Guid AlbumGroupMBID { get; set; } + + [XmlElement("AlbumWmid")] + public Guid AlbumWmid { get; set; } + + [XmlElement("ArtistWmid")] + public Guid ArtistWmid { get; set; } + + [XmlElement("uniqueFileID")] + public string UniqueFileID { get; set; } + + [XmlElement("Source")] + public string Source { get; set; } = "AMG"; + + [XmlElement("SmallCoverArtURL")] + public string SmallCoverArtURL { get; set; } + + [XmlElement("LargeCoverArtURL")] + public string LargeCoverArtURL { get; set; } + + [XmlElement("ReleaseDate")] + public string DateTime { get; set; } + + [XmlElement("Rating")] + public string Rating { get; set; } = " "; + + [XmlElement("PerformerName")] + public string ArtistName { get; set; } + + [XmlElement("MoreInfoLink")] + public string MoreInfoID { get; set; } + + [XmlElement("Label")] + public string LabelName { get; set; } + + // Till the response correctly identifies _which_ files are matches, this must be false. Otherwise, you end up with doubled matches + [XmlElement("IsExactMatch")] + public bool IsExactMatch { get; set; } = false; + + [XmlElement("Genre")] + public string Genre { get; set; } + + [XmlElement("DataProviderParams")] + public string DataProviderParams { get; set; } = "Provider=AMG"; + + [XmlElement("DataProviderLogo")] + public string DataProviderLogo { get; set; } = "Provider=AMG"; + + [XmlElement("BuyNowLink")] + public string BuyNowLink { get; set; } } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs index f1320dd..44d1ce1 100644 --- a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs @@ -10,5 +10,8 @@ public class MdarCdRequestMetadata [XmlElement("MDAR-CD")] public MdarCd MdarCd {get; set;} + + [XmlElement("BackOff")] + public MdarBackoff Backoff = new(); } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs index 01baca4..fb9a092 100644 --- a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs @@ -15,5 +15,8 @@ public class MdarTrack [XmlElement("TrackWmid")] public Guid TrackWmid {get; set;} + + [XmlElement("TrackRequestID")] + public int TrackRequestID {get; set;} = 0; } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs index 2983112..a630f85 100644 --- a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs +++ b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs @@ -23,9 +23,11 @@ public class MdqTrack public int Bitrate; [XmlElement("drmProtected")] - public int DRMProtected; + public int DRMProtected = 0; [XmlElement("trackRequestID")] public int trackRequestId; + [XmlArray("trace")] + public List Trace = new(); } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs index 2566346..5ff575c 100644 --- a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs +++ b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs @@ -5,36 +5,30 @@ namespace Zune.Net.MetaServices.DomainModels.MdrCd [XmlRoot("MDR-CD")] public class MDRCD { + [XmlElement("mdqRequestID")] + public Guid MdqRequestID { get; set; } = Guid.Empty; [XmlElement("uniqueFileID")] public string UniqueFileID { get; set; } - [XmlElement("providerStyle")] - public string ProviderStyle { get; set; } - [XmlElement("publisherRating")] public string PublisherRating { get; set; } + // i.e. providerName=AMG&albumID=DC1A65D9-C8A4-4190-87CB-F871B8AC6D37&a_id=R%20%203021180&album=On%20Empty&artistID=1A9ECD8B-230E-49CD-B97E-1C631530581C&p_id=P%20%202986084&artist=Kevin%20Calder [XmlElement("buyParams")] public string BuyParams { get; set; } - [XmlElement("moreInfoParams")] - public string MoreInfoParams { get; set; } - [XmlElement("dataProvider")] - public string DataProvider { get; set; } + public string DataProvider { get; set; } = "AMG"; [XmlElement("dataProviderParams")] - public string DataProviderParams { get; set; } + public string DataProviderParams { get; set; } = "Provider=AMG"; [XmlElement("dataProviderLogo")] - public string DataProviderLogo { get; set; } + public string DataProviderLogo { get; set; } = "Provider=AMG"; [XmlElement("version")] - public int Version { get; set; } - - [XmlElement("mdqRequestID")] - public Guid MdqRequestID { get; set; } + public string Version { get; set; } = "5.0"; [XmlElement("WMCollectionID")] public Guid WMCollectionID { get; set; } @@ -57,16 +51,29 @@ public class MDRCD [XmlElement("genre")] public string Genre { get; set; } - [XmlElement("needIDs")] - public int NeedIDs { get; set; } + // i.e. "Pop/Rock" + [XmlElement("providerStyle")] + public string AlbumStyle { get; set; } + + [XmlElement("needsIDs")] + public int NeedIDs { get; set; } = 0; + // i.e. 200/drW500/W564/W56460UCJS0.jpg [XmlElement("largeCoverParams")] public string LargeCoverAddress { get; set; } + // i.e. 075/drW500/W564/W56460UCJS0.jpg [XmlElement("smallCoverParams")] public string SmallCoverAddress { get; set; } + // i.e. a_id=R%20%203021180 + [XmlElement("moreInfoParams")] + public string MoreInfoId { get; set; } + [XmlElement("track")] public List Track { get; set; } + + [XmlElement("Volume")] + public int VolumeNumber { get; set; } } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs index 3b881f1..0d36fdb 100644 --- a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs +++ b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs @@ -6,8 +6,10 @@ namespace Zune.Net.MetaServices.DomainModels.MdrCd public class MdrRequestMetadata { + [XmlElement("AlbumId")] + public Guid AlbumId { get; set; } [XmlElement("MDR-CD")] - public MdrCd.MDRCD MDRCD { get; set; } + public MDRCD MDRCD { get; set; } [XmlElement("Backoff")] public Backoff Backoff { get; set; } diff --git a/Zune.Net.MetaServices/Models/WMIS.cs b/Zune.Net.MetaServices/Models/WMIS.cs index 4895e89..5a770d6 100644 --- a/Zune.Net.MetaServices/Models/WMIS.cs +++ b/Zune.Net.MetaServices/Models/WMIS.cs @@ -69,7 +69,7 @@ await Parallel.ForEachAsync(releases, async (release, ct) => } _logger.LogInformation($"Getting all data for MBID: {guid}"); - var release = await _query.LookupReleaseAsync(guid, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); + var release = await _query.LookupReleaseAsync(guid, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.Tags | Include.ArtistCredits | Include.ReleaseGroups); if (release.Title == null) { @@ -84,6 +84,11 @@ await Parallel.ForEachAsync(releases, async (release, ct) => { genre = release.Genres?[0]?.Name; } + else if (release.Tags?.Any() ?? false) + { + genre = release.Tags.ToList().OrderBy(x => x.VoteCount).ToList()[0].Name; + } + if (release.ArtistCredit?.Count > 0) { performerName = release.ArtistCredit?[0]?.Artist.Name; @@ -138,28 +143,37 @@ private async Task> GetTracksFromIReleaseAsync(IRelease release, foreach (var track in release.Media[0].Tracks) { var trackTitle = track.Title ?? "Unknown Title"; - var trackNumber = int.Parse(track.Number ?? "0"); + int trackNumber = 0; + if (track.Number != null && int.TryParse(GetFirstInt().Match(track.Number).Value, out var tryTrackNum)) + { + trackNumber = tryTrackNum; + } + var trackArtist = track.ArtistCredit?[0]?.Name ?? release.ArtistCredit?[0]?.Name ?? "Unknown Artist"; var trackMbid = track.Id; + var trackObj = new MdarTrack() + { + Title = trackTitle, + Performers = trackArtist, + TrackNumber = trackNumber, + TrackWmid = trackMbid + }; + if (requestMetadata != null) { // attempt to bind a trackid to a trackmbid + // Shuld probably also validate track name, string.CompareOrdinal(thisTrackIntId[0].Title.Text, trackTitle) == 0 var thisTrackIntId = requestMetadata.MdqCd.Tracks.Where(x => x.TrackNumber == trackNumber).ToArray(); if (thisTrackIntId != null && thisTrackIntId.Any()) { await _database.CreateTrackReverseLookupRecordAsync(release.Id, trackMbid, thisTrackIntId[0].trackRequestId, thisTrackIntId[0].TrackDurationMs); + trackObj.TrackRequestID = thisTrackIntId[0].trackRequestId; } } - tracks.Add(new MdarTrack() - { - Title = trackTitle, - Performers = trackArtist, - TrackNumber = trackNumber, - TrackWmid = trackMbid - }); + tracks.Add(trackObj); } } return tracks; @@ -176,23 +190,69 @@ public async Task GetMdarCdRequestFromInt64(long albumId, var tracks = await GetTracksFromIReleaseAsync(release, requestMetadata); + // If we have bound a trackRequestID to every track, this is a match + var isExactMatch = tracks.All(x=>x.TrackRequestID > 0); + var intVolume = 1; if (int.TryParse(GetFirstInt().Match(volume).Value, out var tryVolume)) { intVolume = tryVolume; } + var artistMbid = Guid.Empty; + var artistName = string.Empty; + if (release.ArtistCredit?.Any() ?? false) + { + artistMbid = release.ArtistCredit[0].Artist?.Id ?? Guid.Empty; + artistName = release.ArtistCredit[0].Artist?.Name ?? string.Empty; + } + + var label = string.Empty; + if (release.LabelInfo != null) + { + label = release.LabelInfo[0].Label?.Name ?? release.LabelInfo[0].CatalogNumber; + } + + var genre = string.Empty; + if (release.Genres?.Count > 0) + { + genre = release.Genres?[0]?.Name; + } + else if (release.Tags?.Any() ?? false) + { + genre = release.Tags.ToList().OrderBy(x => x.VoteCount).ToList()[0].Name; + } + + var albumArtMbid = "default"; + if (release.CoverArtArchive?.Front ?? false) + { + _logger.LogInformation($"Release {release.Id} HAS ARTWORK"); + albumArtMbid = release.Id.ToString(); + } + return new MdarCdRequestMetadata() { mdqRequestID = release.Id, MdarCd = new MdarCd() { + AId = $"R {albumId}", // will always be 10 characters Title = release.Title, AlbumId = albumId, Volume = intVolume, Items = tracks, AlbumGroupMBID = release.Id, - AlbumMBID = release.Id + AlbumMBID = release.Id, + AlbumWmid = release.Id, + ArtistWmid = artistMbid, + SmallCoverArtURL = albumArtMbid, + LargeCoverArtURL = albumArtMbid, + ArtistName = artistName, + UniqueFileID = $"AMGa_id=R {albumId}", + MoreInfoID = $"a_id=R {albumId}", + LabelName = label, + Genre = genre, + BuyNowLink = albumId.ToString(), + IsExactMatch = isExactMatch } }; } @@ -201,8 +261,9 @@ public async Task GetMdrCdRequestFromTrackIdAsync(int TrackR { var albumMbid = await _database.GetAlbumMbidFromTrackIdAndDurationAsync(TrackRequestID, trackDuration); var trackMbid = await _database.GetTrackMbidFromTrackIdAndDurationAsync(TrackRequestID, trackDuration); + var albumId = await _database.GetAlbumIDFromTrackIdAndDurationAsync(TrackRequestID, trackDuration); - var release = await _query.LookupReleaseAsync(albumMbid.Value, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups); + var release = await _query.LookupReleaseAsync(albumMbid.Value, inc: Include.Labels | Include.DiscIds | Include.Recordings | Include.Genres | Include.ArtistCredits | Include.ReleaseGroups | Include.Tags); var track = release.Media[0].Tracks.Where(x => x.Id == trackMbid).ToList()[0]; var performerName = string.Empty; @@ -231,9 +292,29 @@ public async Task GetMdrCdRequestFromTrackIdAsync(int TrackR { genre = release.Genres?[0]?.Name; } + else if (release.Tags?.Any() ?? false) + { + genre = release.Tags.ToList().OrderBy(x => x.VoteCount).ToList()[0].Name; + } + + var label = string.Empty; + if (release.LabelInfo != null) + { + label = release.LabelInfo[0].Label?.Name ?? release.LabelInfo[0].CatalogNumber; + } + + var albumArtMbid = "default"; + if (release.CoverArtArchive?.Front ?? false) + { + _logger.LogInformation($"Release {release.Id} HAS ARTWORK"); + albumArtMbid = release.Id.ToString(); + } + + var padding = string.Empty.PadRight(8 - albumId.ToString().Length);; return new MdrRequestMetadata() { + AlbumId = release.Id, MdqRequestID = requestId, Backoff = new Backoff() { @@ -241,12 +322,15 @@ public async Task GetMdrCdRequestFromTrackIdAsync(int TrackR }, MDRCD = new MDRCD() { + NeedIDs = 1, MdqRequestID = requestId, WMCollectionGroupID = release.Id, WMCollectionID = release.Id, + //A_id = R xxxx + //P_id = P xxxx Track = new List(){new Track() { - UniqueFileID = trackMbid.Value.ToString("N"), + UniqueFileID = trackMbid.Value.ToString("N"), // AMGp_id=P xxxx;AMGt_id=T xxxxxx TrackRequestID = TrackRequestID, WMContentID = trackMbid.Value.ToString(), TrackTitle = track.Title, @@ -254,14 +338,17 @@ public async Task GetMdrCdRequestFromTrackIdAsync(int TrackR TrackPerformer = trackPerformer, Period = period }}, - UniqueFileID = albumMbid.Value.ToString("N"), - Version = 1, + Label = label, + UniqueFileID = $"AMGa_ID=R {albumId}", AlbumTitle = release.Title, AlbumArtist = performerName, ReleaseDate = release.Date.NearestDate, Genre = genre, - LargeCoverAddress = release.Id.ToString(), - SmallCoverAddress = release.Id.ToString() + AlbumStyle = genre, + LargeCoverAddress = albumArtMbid, + SmallCoverAddress = albumArtMbid, + MoreInfoId = $"R {padding}albumId", + VolumeNumber = 1 } }; } From 9045e4c7f017a5326c5c5348c653980a230d0d85 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Fri, 26 May 2023 12:03:08 -0600 Subject: [PATCH 19/29] splitting the artist controller bits used by the HD out into its own controller --- .../Controllers/Music/ArtistController.cs | 3 +- .../Music/ZuneHDArtistController.cs | 205 ++++++++++++++++++ .../Controllers/AccountController.cs | 3 +- Zune.Net.MetaServices/Controllers/Cover.cs | 2 +- Zune.Net.MetaServices/Controllers/FAI.cs | 1 + .../DomainModels/MDSR-CD/MdsrAlbum.cs | 2 +- Zune.Net.MetaServices/Models/WMIS.cs | 2 +- Zune.Net.Mix/Controllers/ArtistController.cs | 19 ++ Zune.Net.Shared/Helpers/MusicBrainz.Album.cs | 18 +- Zune.Net.Shared/Helpers/MusicBrainz.Track.cs | 5 +- Zune.Net.Shared/Zune.Net.Shared.csproj | 3 +- runDev.sh | 6 +- runProd.sh | 6 +- 13 files changed, 256 insertions(+), 19 deletions(-) create mode 100644 Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs create mode 100644 Zune.Net.Mix/Controllers/ArtistController.cs diff --git a/Zune.Net.Catalog/Controllers/Music/ArtistController.cs b/Zune.Net.Catalog/Controllers/Music/ArtistController.cs index ae6c095..7a5810c 100644 --- a/Zune.Net.Catalog/Controllers/Music/ArtistController.cs +++ b/Zune.Net.Catalog/Controllers/Music/ArtistController.cs @@ -15,7 +15,7 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v{version:decimal}/{culture}/music/artist/")] + [Route("/v3.2/{culture}/music/artist/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ArtistController : Controller { @@ -174,6 +174,7 @@ public async Task>> Images(Guid mbid) } [HttpGet, Route("{mbid}/similarArtists")] + [HttpGet, Route("{mbid}/influencers")] public async Task>> SimilarArtists(Guid mbid) { return await LastFM.GetSimilarArtistsByMBID(mbid); diff --git a/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs b/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs new file mode 100644 index 0000000..3dae547 --- /dev/null +++ b/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs @@ -0,0 +1,205 @@ +using Atom.Xml; +using Flurl.Http; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Linq; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Processing; +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Zune.DB; +using Zune.Net.Helpers; +using Zune.Xml.Catalog; + +namespace Zune.Net.Catalog.Controllers.Music +{ + [Route("/v3.0/{culture}/music/artist/")] + [Produces(Atom.Constants.ATOM_MIMETYPE)] + public class ZuneHDArtistController : Controller + { + private readonly ZuneNetContext _database; + public ZuneHDArtistController(ZuneNetContext database) + { + _database = database; + } + + [HttpGet, Route("{mbid}")] + public async Task> Details(Guid mbid) + { + (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); + Artist artist = MusicBrainz.MBArtistToArtist(mb_artist); + artist.Images = new() { new() { Id = mbid } }; + + if (dc_artist != null) + { + artist.Biography = dc_artist.Value("profile"); + artist.Links.Add(new(Request.Path.Value + "biography", relation: "zune://artist/biography")); + + var dc_artist_image = dc_artist.Value("images")?.FirstOrDefault(i => i.Value("type") == "primary"); + if (dc_artist_image != null) + { + string artistImageUrl = dc_artist_image.Value("uri"); + var artistImageEntry = await _database.AddImageAsync(artistImageUrl); + + artist.BackgroundImage = new() + { + Id = artistImageEntry.Id + }; + } + } + + return artist; + } + + // orderby ReleaseDate|PlayRank optional chunkSize={int} + [HttpGet, Route("{mbid}/tracks")] + public ActionResult> Tracks(Guid mbid, string orderby = "unset", int chunkSize = 10) + { + var tracks = MusicBrainz.GetArtistTracksByMBID(mbid, Request.Path, 100); + var toSort = tracks.Entries; + try + { + switch (orderby.ToLowerInvariant()) + { + case "playrank": + tracks.Entries.Sort((a, b) => a.Popularity.CompareTo(b)); + break; + } + } + catch { } + tracks.Entries = tracks.Entries.Take(chunkSize).ToList(); + return tracks; + } + + // orderby ReleaseDate|PlayRank optional chunkSize={int} + [HttpGet, Route("{mbid}/albums")] + public ActionResult> Albums(Guid mbid, int chunkSize = 10, string orderby = "notset") + { + var feed = MusicBrainz.GetArtistAlbumsByMBID(mbid, Request.Path); + Comparison sortComparer = orderby.ToLowerInvariant() switch + { + "title" => (a, b) => (a.SortTitle ?? a.Title.Value).CompareTo(b.SortTitle ?? b.Title.Value), + + "mostplayed" or "playrank" => (a, b) => a.Popularity.CompareTo(b.Popularity), + //default + _ => (a, b) => a.ReleaseDate.Year.CompareTo(b.ReleaseDate.Year), + }; + feed.Entries.Sort(sortComparer); + feed.Entries = feed.Entries.Take(chunkSize).ToList(); + return feed; + } + + //width=480&resize=true&contenttype=image/jpeg + [HttpGet, Route("{mbid}/deviceBackgroundImage")] + public async Task PrimaryImage(Guid mbid, string contenttype = "image/jpeg", int width = 480, bool resize = true) + { + (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); + if (dc_artist == null) + return StatusCode(404); + + string imgUrl = dc_artist["images"].First(i => i.Value("type") == "primary").Value("uri"); + var imgResponse = await imgUrl.GetAsync(); + if (imgResponse.StatusCode != 200) + return StatusCode(imgResponse.StatusCode); + + var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); + if (resize && image.Size.Width > width) + { + image.Mutate(x => x.Resize(width, 0)); + } + + using var stream = new MemoryStream(); + + if (contenttype.Contains("jpeg")) + { + image.Save(stream, new JpegEncoder()); + } + else if (contenttype.Contains("bmp")) + { + image.Save(stream, new BmpEncoder()); + } + else if (contenttype.Contains("png")) + { + image.Save(stream, new PngEncoder()); + } + + return File(stream.ToArray(), contenttype); + } + + [HttpGet, Route("{mbid}/biography")] + public async Task> Biography(Guid mbid) + { + (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); + if (dc_artist == null) + return StatusCode(404); + DateTime updated = DateTime.Now; + + return new Entry + { + Id = $"tag:catalog.zune.net,1900-01-01:/music/artist/{mbid}/biography", + Title = mb_artist.Name, + Links = { new(Request.Path) }, + Content = Discogs.DCProfileToBiographyContent(dc_artist.Value("profile")), + Updated = updated, + }; + } + + //chunkSize=10 + [HttpGet, Route("{mbid}/images")] + public async Task>> Images(Guid mbid, int chunkSize) + { + (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); + DateTime updated = DateTime.Now; + int dcid = dc_artist.Value("id"); + byte[] zero = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; + + Feed feed = new() + { + Id = $"tag:catalog.zune.net,1900-01-01:/music/artist/{mbid}/images", + Title = mb_artist.Name, + Links = { new(Request.Path) }, + Updated = updated, + }; + + var images = dc_artist.Value("images"); + if (images != null) + { + feed.Entries = images.Select((j, idx) => + { + // Encode DCID and image index in ID + Guid imgId = new(dcid, (short)idx, 0, zero); + + return new Image + { + Id = imgId, + Instances = new() + { + new() + { + Id = imgId, + Url = j.Value("uri"), + Format = "jpg", + Width = j.Value("width"), + Height = j.Value("height"), + } + } + }; + }).Take(chunkSize).ToList(); + } + + return feed; + } + + //chunkSize=10 + [HttpGet, Route("{mbid}/similarArtists")] + public async Task>> SimilarArtists(Guid mbid, int chunkSize) + { + var similar = await LastFM.GetSimilarArtistsByMBID(mbid); + similar.Entries = similar.Entries.Take(chunkSize).ToList(); + return similar; + } + } +} diff --git a/Zune.Net.Commerce/Controllers/AccountController.cs b/Zune.Net.Commerce/Controllers/AccountController.cs index a996264..3be4065 100644 --- a/Zune.Net.Commerce/Controllers/AccountController.cs +++ b/Zune.Net.Commerce/Controllers/AccountController.cs @@ -123,6 +123,7 @@ public ActionResult ZuneUser() } [HttpPost] + [Produces("application/xml")] public ActionResult Balances() { return new BalancesResponse @@ -130,7 +131,7 @@ public ActionResult Balances() Balances = new() { PointsBalance = 8000, - SongCreditBalance = 0, + SongCreditBalance = 100, SongCreditRenewalDate = null } }; diff --git a/Zune.Net.MetaServices/Controllers/Cover.cs b/Zune.Net.MetaServices/Controllers/Cover.cs index 381dd67..c2c0229 100644 --- a/Zune.Net.MetaServices/Controllers/Cover.cs +++ b/Zune.Net.MetaServices/Controllers/Cover.cs @@ -15,7 +15,7 @@ public Cover(ILogger logger) [Route("{coverId}")] public async Task GetCover(string coverId) { - var catalogImageUri = new Uri($"https://coverartarchive.org/release/{coverId}/front-250"); + var catalogImageUri = new Uri($"https://coverartarchive.org/release/{coverId}/front"); if(coverId.Equals("default")) { _logger.LogInformation($"default albumart requested"); diff --git a/Zune.Net.MetaServices/Controllers/FAI.cs b/Zune.Net.MetaServices/Controllers/FAI.cs index 3b6c30a..505bb89 100644 --- a/Zune.Net.MetaServices/Controllers/FAI.cs +++ b/Zune.Net.MetaServices/Controllers/FAI.cs @@ -33,6 +33,7 @@ public async Task Search(string SearchString, string resultTypeStr // UIX Type: ArtistList, of Artist return Ok("SUCCESS"); case "track": + // WMISFAITracksQuery->TrackList (see WMISDATA.SCHEMA.XML) return Ok(); } return NotFound(); diff --git a/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs index 9c3a60b..6f2820f 100644 --- a/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs +++ b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs @@ -8,7 +8,7 @@ public class MdsrAlbum public string Title { get; set; } [XmlElement("id_album")] - public long Id { get; set; } + public long AlbumId { get; set; } [XmlElement("albumPerformer")] public string AlbumArtist { get; set; } diff --git a/Zune.Net.MetaServices/Models/WMIS.cs b/Zune.Net.MetaServices/Models/WMIS.cs index 5a770d6..1bcb66a 100644 --- a/Zune.Net.MetaServices/Models/WMIS.cs +++ b/Zune.Net.MetaServices/Models/WMIS.cs @@ -123,7 +123,7 @@ await Parallel.ForEachAsync(release.Media[0].Tracks, async (track, ct) => { Title = release.Title, BestMatch = bestmatch, - Id = recordId, + AlbumId = recordId, Volume = 1, AlbumArtist = performerName, BuyNowParms = release.Id.ToString(), diff --git a/Zune.Net.Mix/Controllers/ArtistController.cs b/Zune.Net.Mix/Controllers/ArtistController.cs new file mode 100644 index 0000000..4682162 --- /dev/null +++ b/Zune.Net.Mix/Controllers/ArtistController.cs @@ -0,0 +1,19 @@ +using Atom.Xml; +using Microsoft.AspNetCore.Mvc; +using Zune.Net.Helpers; +using Zune.Xml.Catalog; + +namespace Zune.Net.Mix.Controllers +{ + [Route("/v4.0/{culture}/artist/")] + [Produces(Atom.Constants.ATOM_MIMETYPE)] + public class ArtistController : Controller + { + + [HttpGet, Route("{mbid}")] + public async Task>> SimilarArtists(Guid mbid) + { + return await LastFM.GetSimilarArtistsByMBID(mbid); + } + } +} diff --git a/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs b/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs index 7dc0bae..56f0db4 100644 --- a/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs +++ b/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs @@ -69,13 +69,21 @@ public static Album MBReleaseToAlbum(IRelease mb_rel, DateTime? updated = null, if (mb_rel.Media != null && mb_rel.Media.Count > 0) { - var mb_media = mb_rel.Media[0]; - if (mb_media.Tracks != null && mb_media.Tracks.Count > 0) + album.Tracks = new(); + + for(var mediaId = 0; mediaId < mb_rel.Media.Count; mediaId++) { - album.Tracks = new(); - foreach (var mb_track in mb_media.Tracks) - album.Tracks.Add(MBTrackToTrack(mb_track, trackArtist: artist, updated: updated, includeRights: includeRights)); + var mb_media = mb_rel.Media[mediaId]; + + if (mb_media.Tracks != null && mb_media.Tracks.Count > 0) + { + foreach (var mb_track in mb_media.Tracks) + { + album.Tracks.Add(MBTrackToTrack(mb_track, diskNumber: mediaId + 1, trackArtist: artist, updated: updated, includeRights: includeRights)); + } + } } + } if (includeRights) diff --git a/Zune.Net.Shared/Helpers/MusicBrainz.Track.cs b/Zune.Net.Shared/Helpers/MusicBrainz.Track.cs index cd34ac6..915255b 100644 --- a/Zune.Net.Shared/Helpers/MusicBrainz.Track.cs +++ b/Zune.Net.Shared/Helpers/MusicBrainz.Track.cs @@ -41,7 +41,7 @@ public static Track GetTrackByMBID(Guid mbid) var mb_rec = _query.LookupRecording(mbid, Include.Genres | Include.ArtistCredits | Include.Releases | Include.UrlRelationships | Include.Media); return MBRecordingToTrack(mb_rec, includeRights: true); } - catch (QueryException) + catch { // MusicBrainz Picard likes to put the Track ID instead of the Recording ID var releases = _query.BrowseTrackReleases(mbid, limit: 1, inc: Include.UrlRelationships); @@ -89,12 +89,13 @@ public static Track MBRecordingToTrack(IRecording mb_rec, DateTime? updated = nu return track; } - public static Track MBTrackToTrack(ITrack mb_track, MiniArtist trackArtist, DateTime? updated = null, bool includeRights = true) + public static Track MBTrackToTrack(ITrack mb_track, MiniArtist trackArtist, int diskNumber = 1, DateTime? updated = null, bool includeRights = true) { updated ??= DateTime.Now; Track track = new() { + DiscNumber = diskNumber, Id = mb_track.Id.ToString(), Title = mb_track.Title, PrimaryArtist = trackArtist, diff --git a/Zune.Net.Shared/Zune.Net.Shared.csproj b/Zune.Net.Shared/Zune.Net.Shared.csproj index 74ee99f..e3f6185 100644 --- a/Zune.Net.Shared/Zune.Net.Shared.csproj +++ b/Zune.Net.Shared/Zune.Net.Shared.csproj @@ -21,6 +21,7 @@ + @@ -28,4 +29,4 @@ - + \ No newline at end of file diff --git a/runDev.sh b/runDev.sh index f371ac6..84ca93c 100755 --- a/runDev.sh +++ b/runDev.sh @@ -1,4 +1,4 @@ #!/bin/bash -docker-compose stop -docker-compose build -docker-compose up \ No newline at end of file +docker compose stop +docker compose build +docker compose up \ No newline at end of file diff --git a/runProd.sh b/runProd.sh index 01c3490..aaaa8d7 100755 --- a/runProd.sh +++ b/runProd.sh @@ -1,4 +1,4 @@ #!/bin/bash -docker-compose stop -docker-compose build -docker-compose up -d --remove-orphans +docker compose stop +docker compose build +docker compose up -d --remove-orphans From 77e9aea8518d0bc8938f821d56050562b55b64f5 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Fri, 26 May 2023 13:31:08 -0600 Subject: [PATCH 20/29] split out the HD's image controller methods. Probably should use the same resizing routines for the v3.2 endpoints --- .../Controllers/ImageController.cs | 8 +- .../Controllers/ZuneHDImageController.cs | 151 ++++++++++++++++++ Zune.Net.Catalog.Image/Program.cs | 2 + .../Music/ZuneHDArtistController.cs | 37 ----- Zune.Net.Catalog/Startup.cs | 2 +- Zune.Net.Commerce/Program.cs | 2 + Zune.Net.Inbox/Startup.cs | 2 +- Zune.Net.Login/Program.cs | 1 + Zune.Net.MetaServices/Program.cs | 2 + Zune.Net.Mix/Program.cs | 2 +- Zune.Net.SocialApi/Startup.cs | 2 +- Zune.Net.Tiles/Program.cs | 2 + Zune.Net.Tuners/Program.cs | 2 + docker-compose.yml | 2 +- 14 files changed, 171 insertions(+), 46 deletions(-) create mode 100644 Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs diff --git a/Zune.Net.Catalog.Image/Controllers/ImageController.cs b/Zune.Net.Catalog.Image/Controllers/ImageController.cs index 6152808..4440b8f 100644 --- a/Zune.Net.Catalog.Image/Controllers/ImageController.cs +++ b/Zune.Net.Catalog.Image/Controllers/ImageController.cs @@ -11,7 +11,7 @@ namespace Zune.Net.Catalog.Image.Controllers { - [Route("/v{version:decimal}/{culture}/")] + [Route("/v3.2/{culture}/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ImageController : Controller { @@ -127,8 +127,8 @@ public async Task ArtistImage(Guid id, string type) _logger.LogInformation($"Getting image from RecordingID"); var albumId = MusicBrainz.GetAlbumByRecordingId(id).Id; _logger.LogInformation($"Got ID{albumId}"); - //e.g.: http://coverartarchive.org/release/93c488f3-1739-455d-a609-85b846b45344/front-250 - imageUrl = new Uri($"http://coverartarchive.org/release/{albumId}/front-250"); + imageUrl = new Uri($"https://coverartarchive.org/release/{id}/front"); + } catch (Exception e) { _logger.LogError(e, "Failed to get AlbumID"); @@ -137,7 +137,7 @@ public async Task ArtistImage(Guid id, string type) if(imageUrl == null) { _logger.LogInformation($"Getting image failed, falling back"); - imageUrl = new Uri($"http://coverartarchive.org/release/{id}/front-250"); + imageUrl = new Uri($"http://coverartarchive.org/release/{id}/front"); } _logger.LogInformation($"Getting image from: {imageUrl.AbsoluteUri}"); diff --git a/Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs b/Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs new file mode 100644 index 0000000..14e1895 --- /dev/null +++ b/Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Flurl.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Processing; +using Zune.DB; +using Zune.Net.Helpers; + +namespace Zune.Net.Catalog.Image +{ + [Route("/v3.0/{culture}/")] + public class ZuneHDArtistImageController : Controller + { + private static readonly Uri defaultImage = new Uri("https://hellofromseattle.com/wp-content/uploads/sites/6/2020/03/Zune-Basic.png"); + + private ILogger _logger; + private static readonly ConcurrentDictionary dcArtistCache = new(); + + private static readonly int[] caaSupportedSizes = new[] { 250, 500, 1200 }; + + + private ZuneNetContext _database; + public ZuneHDArtistImageController(ZuneNetContext database, ILogger logger) + { + _logger = logger; + _database = database; + } + + [HttpGet, Route("image/{id}")] + public async Task Image(Guid id, bool resize = true, int width = 480, string contenttype = "image/jpeg") + { + string? imageUrl = null; + + (var idA, var idB, var idC) = id.GetGuidParts(); + var imageEntry = await _database.GetImageEntryAsync(id); + + if (imageEntry != null) + { + imageUrl = imageEntry.Url; + } + else if (idC == 0) + { + int dcid = unchecked((int)idA); + + // Get or update cached artist + if (!dcArtistCache.TryGetValue(dcid, out var dc_artist)) + { + // Artist not in cache + dc_artist = await Discogs.GetDCArtistByDCID(dcid); + dcArtistCache.AddOrUpdate(dcid, _ => dc_artist, (_, _) => dc_artist); + } + + // Get URL for requested image + var images = dc_artist.Value("images"); + if (images != null && images.Count > idB) + { + var thisImage = images[idB]; + imageUrl = thisImage.Value("uri"); + } + } + else + { + try + { + (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(id); + if (dc_artist != null) + imageUrl = dc_artist["images"].First(i => i.Value("type") == "primary").Value("uri"); + } + catch + { + imageUrl = $"https://coverartarchive.org/release/{id}/front"; + } + } + + var imgResponse = await imageUrl.GetAsync(); + if (imgResponse.StatusCode != 200) + { + return StatusCode(imgResponse.StatusCode); + } + + var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); + if (resize && image.Size.Width > width) + { + image.Mutate(x => x.Resize(width, 0)); + } + + using var stream = new MemoryStream(); + + if (contenttype.Contains("jpeg")) + { + image.Save(stream, new JpegEncoder()); + } + else if (contenttype.Contains("bmp")) + { + image.Save(stream, new BmpEncoder()); + } + else if (contenttype.Contains("png")) + { + image.Save(stream, new PngEncoder()); + } + + return File(stream.ToArray(), contenttype); + } + + //width=480&resize=true&contenttype=image/jpeg + [HttpGet, Route("{mbid}/deviceBackgroundImage")] + public async Task PrimaryImage(Guid mbid, string contenttype = "image/jpeg", int width = 480, bool resize = true) + { + (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); + if (dc_artist == null) + return StatusCode(404); + + string imgUrl = dc_artist["images"].First(i => i.Value("type") == "primary").Value("uri"); + var imgResponse = await imgUrl.GetAsync(); + if (imgResponse.StatusCode != 200) + return StatusCode(imgResponse.StatusCode); + + var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); + if (resize && image.Size.Width > width) + { + image.Mutate(x => x.Resize(width, 0)); + } + + using var stream = new MemoryStream(); + + if (contenttype.Contains("jpeg")) + { + image.Save(stream, new JpegEncoder()); + } + else if (contenttype.Contains("bmp")) + { + image.Save(stream, new BmpEncoder()); + } + else if (contenttype.Contains("png")) + { + image.Save(stream, new PngEncoder()); + } + + return File(stream.ToArray(), contenttype); + } + } +} \ No newline at end of file diff --git a/Zune.Net.Catalog.Image/Program.cs b/Zune.Net.Catalog.Image/Program.cs index d20d4a7..ea343a7 100644 --- a/Zune.Net.Catalog.Image/Program.cs +++ b/Zune.Net.Catalog.Image/Program.cs @@ -26,6 +26,8 @@ public static void Main(string[] args) var app = builder.Build(); + app.UseHttpLogging(); + // Configure the HTTP request pipeline. app.UseRouting(); diff --git a/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs b/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs index 3dae547..b2c7b8a 100644 --- a/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs +++ b/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs @@ -92,43 +92,6 @@ public ActionResult> Albums(Guid mbid, int chunkSize = 10, string or return feed; } - //width=480&resize=true&contenttype=image/jpeg - [HttpGet, Route("{mbid}/deviceBackgroundImage")] - public async Task PrimaryImage(Guid mbid, string contenttype = "image/jpeg", int width = 480, bool resize = true) - { - (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); - if (dc_artist == null) - return StatusCode(404); - - string imgUrl = dc_artist["images"].First(i => i.Value("type") == "primary").Value("uri"); - var imgResponse = await imgUrl.GetAsync(); - if (imgResponse.StatusCode != 200) - return StatusCode(imgResponse.StatusCode); - - var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); - if (resize && image.Size.Width > width) - { - image.Mutate(x => x.Resize(width, 0)); - } - - using var stream = new MemoryStream(); - - if (contenttype.Contains("jpeg")) - { - image.Save(stream, new JpegEncoder()); - } - else if (contenttype.Contains("bmp")) - { - image.Save(stream, new BmpEncoder()); - } - else if (contenttype.Contains("png")) - { - image.Save(stream, new PngEncoder()); - } - - return File(stream.ToArray(), contenttype); - } - [HttpGet, Route("{mbid}/biography")] public async Task> Biography(Guid mbid) { diff --git a/Zune.Net.Catalog/Startup.cs b/Zune.Net.Catalog/Startup.cs index 4cac177..f8d959b 100644 --- a/Zune.Net.Catalog/Startup.cs +++ b/Zune.Net.Catalog/Startup.cs @@ -50,7 +50,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseDeveloperExceptionPage(); } - //app.UseHttpsRedirection(); + app.UseHttpLogging(); app.UseRequestBuffering(); diff --git a/Zune.Net.Commerce/Program.cs b/Zune.Net.Commerce/Program.cs index c385da0..5916c4e 100644 --- a/Zune.Net.Commerce/Program.cs +++ b/Zune.Net.Commerce/Program.cs @@ -27,6 +27,8 @@ public static void Main(string[] args) var app = builder.Build(); + app.UseHttpLogging(); + // Configure the HTTP request pipeline. app.UseRequestBuffering(); diff --git a/Zune.Net.Inbox/Startup.cs b/Zune.Net.Inbox/Startup.cs index 552e4d1..05e5892 100644 --- a/Zune.Net.Inbox/Startup.cs +++ b/Zune.Net.Inbox/Startup.cs @@ -37,7 +37,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseDeveloperExceptionPage(); } - app.UseHttpsRedirection(); + app.UseHttpLogging(); app.UseRouting(); diff --git a/Zune.Net.Login/Program.cs b/Zune.Net.Login/Program.cs index 9de59f3..3e82cf2 100644 --- a/Zune.Net.Login/Program.cs +++ b/Zune.Net.Login/Program.cs @@ -17,6 +17,7 @@ public static void Main(string[] args) builder.Services.AddControllers(); var app = builder.Build(); + app.UseHttpLogging(); // Configure the HTTP request pipeline. diff --git a/Zune.Net.MetaServices/Program.cs b/Zune.Net.MetaServices/Program.cs index 5ec199e..913cb35 100644 --- a/Zune.Net.MetaServices/Program.cs +++ b/Zune.Net.MetaServices/Program.cs @@ -17,6 +17,8 @@ var app = builder.Build(); +app.UseHttpLogging(); + app.MapControllers(); app.UseResponseCaching(); diff --git a/Zune.Net.Mix/Program.cs b/Zune.Net.Mix/Program.cs index 3fced54..7928abe 100644 --- a/Zune.Net.Mix/Program.cs +++ b/Zune.Net.Mix/Program.cs @@ -12,7 +12,7 @@ // Configure the HTTP request pipeline. -//app.UseHttpsRedirection(); +app.UseHttpLogging(); app.UseAuthorization(); diff --git a/Zune.Net.SocialApi/Startup.cs b/Zune.Net.SocialApi/Startup.cs index 316d791..5ebc78f 100644 --- a/Zune.Net.SocialApi/Startup.cs +++ b/Zune.Net.SocialApi/Startup.cs @@ -39,7 +39,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseDeveloperExceptionPage(); } - //app.UseHttpsRedirection(); + app.UseHttpLogging(); app.UseRequestBuffering(); diff --git a/Zune.Net.Tiles/Program.cs b/Zune.Net.Tiles/Program.cs index ac7c10a..335b244 100644 --- a/Zune.Net.Tiles/Program.cs +++ b/Zune.Net.Tiles/Program.cs @@ -6,6 +6,8 @@ var app = builder.Build(); +app.UseHttpLogging(); + // Configure the HTTP request pipeline. app.UseHttpsRedirection(); diff --git a/Zune.Net.Tuners/Program.cs b/Zune.Net.Tuners/Program.cs index 4e477c3..38e7efc 100644 --- a/Zune.Net.Tuners/Program.cs +++ b/Zune.Net.Tuners/Program.cs @@ -4,6 +4,8 @@ var app = builder.Build(); +app.UseHttpLogging(); + app.MapGet("/{locale}/ZunePCClient/{version}/{file}.xml", (string locale, string version, string file) => { string filePath = Path.Combine(app.Environment.ContentRootPath, "Resources", file + ".xml"); diff --git a/docker-compose.yml b/docker-compose.yml index 9b018c4..fce6a0b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ networks: services: mongodb: - restart: unless-stopped + restart: always image: mongo:latest environment: MONGO_INITDB_ROOT_USERNAME: root From 0275a76d6e0b7694eb0e319e35aaa2cc24c95317 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Fri, 26 May 2023 13:53:25 -0600 Subject: [PATCH 21/29] logging is important kids. This time, it was cause i didn't have a valid route. DOH. --- .../Controllers/ZuneHDImageController.cs | 29 +++++++++++++++++-- .../Music/ZuneHDArtistController.cs | 9 +++++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs b/Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs index 14e1895..c581d1a 100644 --- a/Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs +++ b/Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs @@ -46,37 +46,43 @@ public async Task Image(Guid id, bool resize = true, int width = if (imageEntry != null) { imageUrl = imageEntry.Url; + _logger.LogDebug("using database backed image"); } else if (idC == 0) { + // wtf is this??? int dcid = unchecked((int)idA); // Get or update cached artist if (!dcArtistCache.TryGetValue(dcid, out var dc_artist)) { - // Artist not in cache + _logger.LogDebug("artist not in cache, adding"); dc_artist = await Discogs.GetDCArtistByDCID(dcid); dcArtistCache.AddOrUpdate(dcid, _ => dc_artist, (_, _) => dc_artist); } // Get URL for requested image + _logger.LogDebug("getting discogs artist images"); var images = dc_artist.Value("images"); if (images != null && images.Count > idB) { + _logger.LogDebug("got discogs artist image, sending"); var thisImage = images[idB]; imageUrl = thisImage.Value("uri"); } } - else + if (string.IsNullOrEmpty(imageUrl)) { try { + _logger.LogDebug("Trying discogs artistID"); (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(id); if (dc_artist != null) imageUrl = dc_artist["images"].First(i => i.Value("type") == "primary").Value("uri"); } catch { + _logger.LogDebug("using coverart archive"); imageUrl = $"https://coverartarchive.org/release/{id}/front"; } } @@ -84,12 +90,14 @@ public async Task Image(Guid id, bool resize = true, int width = var imgResponse = await imageUrl.GetAsync(); if (imgResponse.StatusCode != 200) { + _logger.LogDebug("failed to fetch image"); return StatusCode(imgResponse.StatusCode); } var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); if (resize && image.Size.Width > width) { + _logger.LogDebug("resizing"); image.Mutate(x => x.Resize(width, 0)); } @@ -97,14 +105,17 @@ public async Task Image(Guid id, bool resize = true, int width = if (contenttype.Contains("jpeg")) { + _logger.LogDebug("sending as jpg"); image.Save(stream, new JpegEncoder()); } else if (contenttype.Contains("bmp")) { + _logger.LogDebug("bmp"); image.Save(stream, new BmpEncoder()); } else if (contenttype.Contains("png")) { + _logger.LogDebug("sending as png"); image.Save(stream, new PngEncoder()); } @@ -112,21 +123,30 @@ public async Task Image(Guid id, bool resize = true, int width = } //width=480&resize=true&contenttype=image/jpeg - [HttpGet, Route("{mbid}/deviceBackgroundImage")] + [HttpGet("music/artist/{mbid}/deviceBackgroundImage")] public async Task PrimaryImage(Guid mbid, string contenttype = "image/jpeg", int width = 480, bool resize = true) { + _logger.LogDebug("getting by mbid"); (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); if (dc_artist == null) + { + _logger.LogDebug("failed to fetch by mbid"); return StatusCode(404); + } + _logger.LogDebug("getting primary image"); string imgUrl = dc_artist["images"].First(i => i.Value("type") == "primary").Value("uri"); var imgResponse = await imgUrl.GetAsync(); if (imgResponse.StatusCode != 200) + { + _logger.LogDebug("failed to fetch image uri"); return StatusCode(imgResponse.StatusCode); + } var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); if (resize && image.Size.Width > width) { + _logger.LogDebug("resizing"); image.Mutate(x => x.Resize(width, 0)); } @@ -134,14 +154,17 @@ public async Task PrimaryImage(Guid mbid, string contenttype = "im if (contenttype.Contains("jpeg")) { + _logger.LogDebug("sending as jpg"); image.Save(stream, new JpegEncoder()); } else if (contenttype.Contains("bmp")) { + _logger.LogDebug("bmp"); image.Save(stream, new BmpEncoder()); } else if (contenttype.Contains("png")) { + _logger.LogDebug("sending as png"); image.Save(stream, new PngEncoder()); } diff --git a/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs b/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs index b2c7b8a..23535ab 100644 --- a/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs +++ b/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs @@ -1,6 +1,7 @@ using Atom.Xml; using Flurl.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Jpeg; @@ -21,8 +22,10 @@ namespace Zune.Net.Catalog.Controllers.Music public class ZuneHDArtistController : Controller { private readonly ZuneNetContext _database; - public ZuneHDArtistController(ZuneNetContext database) + private readonly ILogger _logger; + public ZuneHDArtistController(ILogger logger, ZuneNetContext database) { + _logger = logger; _database = database; } @@ -116,6 +119,10 @@ public async Task>> Images(Guid mbid, int chunkSize) { (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); DateTime updated = DateTime.Now; + if(dc_artist == null) + { + return StatusCode(404); + } int dcid = dc_artist.Value("id"); byte[] zero = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; From 3063940f21e5a4d61fb6ccbf3371089f4c245397 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Fri, 26 May 2023 13:57:46 -0600 Subject: [PATCH 22/29] differentiate API versions a bit better --- Zune.DB.Console/Program.cs | 3 +++ Zune.Net.Catalog/Controllers/Music/AlbumController.cs | 5 +++-- Zune.Net.Catalog/Controllers/Music/ChartController.cs | 3 ++- Zune.Net.Catalog/Controllers/Music/FeaturesController.cs | 3 ++- Zune.Net.Catalog/Controllers/Music/GenreController.cs | 3 ++- Zune.Net.Catalog/Controllers/Music/TrackController.cs | 3 ++- Zune.Net.Catalog/Controllers/Podcast/ChartController.cs | 2 +- Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs | 3 ++- 8 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Zune.DB.Console/Program.cs b/Zune.DB.Console/Program.cs index 3a3d691..aae365a 100644 --- a/Zune.DB.Console/Program.cs +++ b/Zune.DB.Console/Program.cs @@ -43,6 +43,9 @@ static async Task Main(string[] args) DatabaseName = dbName, }); + await ctx.ClearAlbumLookupAsync(); + return; + await ctx.ClearMembersAsync(); // await ctx.ClearTokensAsync(); // await ctx.ClearImagesAsync(); diff --git a/Zune.Net.Catalog/Controllers/Music/AlbumController.cs b/Zune.Net.Catalog/Controllers/Music/AlbumController.cs index 299b003..6531373 100644 --- a/Zune.Net.Catalog/Controllers/Music/AlbumController.cs +++ b/Zune.Net.Catalog/Controllers/Music/AlbumController.cs @@ -6,7 +6,8 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v{version:decimal}/{culture}/music/album/")] + [Route("/v3.2/{culture}/music/album/")] // Desktop app + [Route("/v3.0/{culture}/music/album/")] // Zune HD [Produces(Atom.Constants.ATOM_MIMETYPE)] public class AlbumController : Controller { @@ -19,7 +20,7 @@ public ActionResult> Search() return MusicBrainz.SearchAlbums(queries[0], Request.Path); } - + //"GET /v3.2/en-US/music/album/c7153846-046f-41b7-a9cd-a9d10751ae60 HTTP/1.1" 200 42271 "-" "-" [HttpGet("{mbid}"), HttpGet("details/{mbid}")] public ActionResult Details(Guid mbid) { diff --git a/Zune.Net.Catalog/Controllers/Music/ChartController.cs b/Zune.Net.Catalog/Controllers/Music/ChartController.cs index db864df..79c4319 100644 --- a/Zune.Net.Catalog/Controllers/Music/ChartController.cs +++ b/Zune.Net.Catalog/Controllers/Music/ChartController.cs @@ -8,7 +8,8 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v{version:decimal}/{culture}/music/chart/zune/")] + [Route("/v3.2/{culture}/music/chart/zune/")] + [Route("/v3.0/{culture}/music/chart/zune/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ChartController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs b/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs index 38eecc9..28a85bc 100644 --- a/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs +++ b/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs @@ -6,7 +6,8 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v{version:decimal}/{culture}/music/features/")] + [Route("/v3.2/{culture}/music/features/")] + [Route("/v3.0/{culture}/music/features/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class FeaturesController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Music/GenreController.cs b/Zune.Net.Catalog/Controllers/Music/GenreController.cs index 9ebbd89..bbfd97c 100644 --- a/Zune.Net.Catalog/Controllers/Music/GenreController.cs +++ b/Zune.Net.Catalog/Controllers/Music/GenreController.cs @@ -6,7 +6,8 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v{version:decimal}/{culture}/music/genre/")] + [Route("/v3.2/{culture}/music/genre/")] + [Route("/v3.0/{culture}/music/genre/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class GenreController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Music/TrackController.cs b/Zune.Net.Catalog/Controllers/Music/TrackController.cs index f853649..3763ea5 100644 --- a/Zune.Net.Catalog/Controllers/Music/TrackController.cs +++ b/Zune.Net.Catalog/Controllers/Music/TrackController.cs @@ -9,7 +9,8 @@ namespace Zune.Net.Catalog.Controllers.Music { - [Route("/v{version:decimal}/{culture}/music/track/")] + [Route("/v3.2/{culture}/music/track/")] + [Route("/v3.0/{culture}/music/track/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class TrackController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs b/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs index 7217bfc..9d2cb68 100644 --- a/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs +++ b/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs @@ -7,7 +7,7 @@ namespace Zune.Net.Catalog.Controllers.Podcast { - [Route("/v{version:decimal}/{culture}/podcastchart/zune/")] + [Route("/v3.2/{culture}/podcastchart/zune/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ChartController : Controller { diff --git a/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs b/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs index 47c25c2..4364b81 100644 --- a/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs +++ b/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs @@ -9,7 +9,8 @@ namespace Zune.Net.Catalog.Controllers.Podcast { - [Route("/v{version:decimal}/{culture}/")] + [Route("/v3.0/{culture}/")] + [Route("/v3.2/{culture}/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class PodcastController : Controller { From b978f58ca89cfd94e992b4385dbf81c2da09ae31 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Tue, 30 May 2023 00:22:27 -0600 Subject: [PATCH 23/29] Consolidating Image controller, fixing a strange crash in zune.exe when the tracking gif is missing by adding one. --- .../Controllers/ImageController.cs | 185 ++++++++++-------- .../Controllers/ZuneHDImageController.cs | 174 ---------------- Zune.Net.Catalog.Image/Program.cs | 2 +- .../appsettings.Development.json | 6 +- Zune.Net.Catalog/Program.cs | 6 - Zune.Net.Commerce/Program.cs | 2 +- .../Properties/launchSettings.json | 27 --- .../Controllers/MessagingInboxController.cs | 16 +- Zune.Net.Inbox/Program.cs | 38 ++-- Zune.Net.Inbox/Properties/launchSettings.json | 27 --- Zune.Net.Inbox/Startup.cs | 9 +- Zune.Net.Login/Program.cs | 2 +- Zune.Net.MetaServices/Program.cs | 2 +- Zune.Net.Mix/Program.cs | 2 +- .../Controllers/MembersController.cs | 11 +- .../Controllers/WebStatsController.cs | 28 +++ .../Properties/launchSettings.json | 27 --- Zune.Net.SocialApi/Resources/1x1px.gif | Bin 0 -> 842 bytes Zune.Net.SocialApi/Startup.cs | 2 +- Zune.Net.SocialApi/Zune.Net.SocialApi.csproj | 9 +- Zune.Net.Tiles/Program.cs | 4 +- Zune.Net.Tuners/Program.cs | 2 +- runProd.sh | 1 + 23 files changed, 185 insertions(+), 397 deletions(-) delete mode 100644 Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs delete mode 100644 Zune.Net.Commerce/Properties/launchSettings.json delete mode 100644 Zune.Net.Inbox/Properties/launchSettings.json create mode 100644 Zune.Net.SocialApi/Controllers/WebStatsController.cs delete mode 100644 Zune.Net.SocialApi/Properties/launchSettings.json create mode 100644 Zune.Net.SocialApi/Resources/1x1px.gif diff --git a/Zune.Net.Catalog.Image/Controllers/ImageController.cs b/Zune.Net.Catalog.Image/Controllers/ImageController.cs index 4440b8f..927f90b 100644 --- a/Zune.Net.Catalog.Image/Controllers/ImageController.cs +++ b/Zune.Net.Catalog.Image/Controllers/ImageController.cs @@ -1,8 +1,14 @@ -using Microsoft.AspNetCore.Mvc; +using Flurl.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Processing; using System; using System.Collections.Concurrent; +using System.IO; using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -11,13 +17,13 @@ namespace Zune.Net.Catalog.Image.Controllers { + [Route("/test/{culture}/")] [Route("/v3.2/{culture}/")] + [Route("/v3.0/{culture}/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ImageController : Controller { - private static readonly Uri defaultImage = new Uri("https://hellofromseattle.com/wp-content/uploads/sites/6/2020/03/Zune-Basic.png"); private static readonly ConcurrentDictionary dcArtistCache = new(); - private static readonly int[] caaSupportedSizes = new[] { 250, 500, 1200 }; private readonly ZuneNetContext _database; private readonly ILogger _logger; @@ -28,132 +34,143 @@ public ImageController(ZuneNetContext database, ILogger logger) } [HttpGet, Route("image/{id}")] - public async Task Image(Guid id) + public async Task Image(Guid id, bool resize = true, int width = 480, string contenttype = "image/jpeg") { string? imageUrl = null; (var idA, var idB, var idC) = id.GetGuidParts(); - var imageEntry = await _database.GetImageEntryAsync(id); - if (imageEntry != null) - { - imageUrl = imageEntry.Url; - } - else if (idC == 0) + if (idC == 0) { + // do better. this is... sketch. int dcid = unchecked((int)idA); // Get or update cached artist if (!dcArtistCache.TryGetValue(dcid, out var dc_artist)) { - // Artist not in cache + _logger.LogDebug("artist not in cache, adding"); dc_artist = await Discogs.GetDCArtistByDCID(dcid); dcArtistCache.AddOrUpdate(dcid, _ => dc_artist, (_, _) => dc_artist); } // Get URL for requested image + _logger.LogDebug("getting discogs artist images"); var images = dc_artist.Value("images"); if (images != null && images.Count > idB) { - var image = images[idB]; - imageUrl = image.Value("uri"); + _logger.LogDebug("got discogs artist image, sending"); + var thisImage = images[idB]; + imageUrl = thisImage.Value("uri"); } - } - else - { - // The Cover Art Archive API supports sizes of 250, 500, and 1200 - int requestedWidth = 500; - if (Request.Query.TryGetValue("width", out var widthValues) && widthValues.Count > 0) - int.TryParse(widthValues[0], out requestedWidth); - - int width = caaSupportedSizes.MinBy(x => Math.Abs(x - requestedWidth)); - imageUrl = $"https://coverartarchive.org/release/{id}/front-{width}"; - } - - if(Request.Query.TryGetValue("resize", out var resize)) + } else { - if(bool.TryParse(resize, out var _resize)) + try { - if(_resize) // this flag basically states the zune is expecting the image to be the payload, so we hackily respond with that + var imageEntry = await _database.GetImageEntryAsync(id); + if (imageEntry != null) { - _logger.LogInformation("streaming the image to the zune now!"); - // Probably should pull the contenttype value out of the query... - using var client = new HttpClient(); - var result = await client.GetAsync(imageUrl); - if(!result.IsSuccessStatusCode) + if (!string.IsNullOrEmpty(imageEntry.Url)) { - _logger.LogInformation("Falling back to the default image, failed to resolve actual artwork"); - result = await client.GetAsync(defaultImage); + _logger.LogDebug("using database backed image"); + imageUrl = imageEntry.Url; } - return new HttpResponseMessageResult(result); } - } + } catch {} + } + + if(string.IsNullOrEmpty(imageUrl)) + { + return await ArtistImage(id, "failoverFromPrimaryEndpoint", resize, width, contenttype); } - // Request the image from the API and forward it to the Zune software - return imageUrl != null - ? Redirect(imageUrl) - : NotFound(); + return await ReturnResizedImageAsync(imageUrl, resize, width, contenttype); } // i.e. http://image.catalog.zune.net/v3.0/en-US/music/track/f32bb0ab-59d6-4620-b239-e86dc68647a4/albumImage?width=240&height=240&resize=true [HttpGet, Route("music/{imageKind}/{id}/{type}")] - public async Task ArtistImage(Guid id, string type) + public async Task ArtistImage(Guid id, string type, bool resize = true, int width = 480, string contenttype = "image/jpeg") { - Uri? imageUrl = null; + _logger.LogDebug($"Fetching image type: '{type}', starting with DC"); + // known types - deviceBackgroundImage, primaryImage, albumImage. + // primaryImage is mostly what the Zune app asks for + // deviceBackgroundImage is what is displayed on the 'now playing' screen + // albumImage is just the album cover. + + string imageUrl; + try + { + imageUrl = await GetImageUrlFromDCAsync(id); + } + catch + { + _logger.LogDebug("DC failed for some reason, so we are now going to try Cover Archive"); + imageUrl = GetImageUrlFromCoverArchive(id); + } + + return await ReturnResizedImageAsync(imageUrl, resize, width, contenttype); + } + + private async Task ReturnResizedImageAsync(string imageUrl, bool resize, int width, string contenttype) + { + var imgResponse = await imageUrl.GetAsync(); - if (type == "primaryImage") + if (imgResponse.StatusCode != 200) { - try - { - _logger.LogInformation($"Getting image for Artist"); - (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(id); + NotFound(); + } - imageUrl = new Uri(dc_artist.Value("images")? - .FirstOrDefault(i => i.Value("type") == "primary")? - .Value("uri")); - } catch - { - _logger.LogInformation($"Failed to get by Artist ID, failing over to albumImage lookup method"); - type = "albumImage"; - } + var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); + if (resize && image.Size.Width > width) + { + _logger.LogDebug("resizing"); + image.Mutate(x => x.Resize(width, 0)); + } + + using var stream = new MemoryStream(); + if (contenttype.Contains("jpeg")) + { + _logger.LogDebug("sending as jpg"); + image.Save(stream, new JpegEncoder()); } - if(type == "albumImage") + else if (contenttype.Contains("bmp")) { - try - { - _logger.LogInformation($"Getting image from RecordingID"); - var albumId = MusicBrainz.GetAlbumByRecordingId(id).Id; - _logger.LogInformation($"Got ID{albumId}"); - imageUrl = new Uri($"https://coverartarchive.org/release/{id}/front"); + _logger.LogDebug("bmp"); + image.Save(stream, new BmpEncoder()); + } + else if (contenttype.Contains("png")) + { + _logger.LogDebug("sending as png"); + image.Save(stream, new PngEncoder()); + } - } catch (Exception e) - { - _logger.LogError(e, "Failed to get AlbumID"); - } + return File(stream.ToArray(), contenttype); + } - if(imageUrl == null) - { - _logger.LogInformation($"Getting image failed, falling back"); - imageUrl = new Uri($"http://coverartarchive.org/release/{id}/front"); - } + private static async Task GetImageUrlFromDCAsync(Guid id) + { + (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(id); - _logger.LogInformation($"Getting image from: {imageUrl.AbsoluteUri}"); + return dc_artist.Value("images")? + .FirstOrDefault(i => i.Value("type") == "primary")? + .Value("uri"); + } - using var client = new HttpClient(); - var result = await client.GetAsync(imageUrl); - if(!result.IsSuccessStatusCode) - { - result = await client.GetAsync(defaultImage); - } - return new HttpResponseMessageResult(result); + private static string GetImageUrlFromCoverArchive(Guid id) + { + string? albumId = null; + try + { + albumId = MusicBrainz.GetAlbumByRecordingId(id).Id; + } catch + { } - - return imageUrl != null - ? Redirect(imageUrl.AbsoluteUri) - : NotFound(); + if(string.IsNullOrEmpty(albumId)) + { + albumId = id.ToString(); + } + return $"https://coverartarchive.org/release/{albumId}/front"; } } } diff --git a/Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs b/Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs deleted file mode 100644 index c581d1a..0000000 --- a/Zune.Net.Catalog.Image/Controllers/ZuneHDImageController.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; -using Flurl.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json.Linq; -using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.Processing; -using Zune.DB; -using Zune.Net.Helpers; - -namespace Zune.Net.Catalog.Image -{ - [Route("/v3.0/{culture}/")] - public class ZuneHDArtistImageController : Controller - { - private static readonly Uri defaultImage = new Uri("https://hellofromseattle.com/wp-content/uploads/sites/6/2020/03/Zune-Basic.png"); - - private ILogger _logger; - private static readonly ConcurrentDictionary dcArtistCache = new(); - - private static readonly int[] caaSupportedSizes = new[] { 250, 500, 1200 }; - - - private ZuneNetContext _database; - public ZuneHDArtistImageController(ZuneNetContext database, ILogger logger) - { - _logger = logger; - _database = database; - } - - [HttpGet, Route("image/{id}")] - public async Task Image(Guid id, bool resize = true, int width = 480, string contenttype = "image/jpeg") - { - string? imageUrl = null; - - (var idA, var idB, var idC) = id.GetGuidParts(); - var imageEntry = await _database.GetImageEntryAsync(id); - - if (imageEntry != null) - { - imageUrl = imageEntry.Url; - _logger.LogDebug("using database backed image"); - } - else if (idC == 0) - { - // wtf is this??? - int dcid = unchecked((int)idA); - - // Get or update cached artist - if (!dcArtistCache.TryGetValue(dcid, out var dc_artist)) - { - _logger.LogDebug("artist not in cache, adding"); - dc_artist = await Discogs.GetDCArtistByDCID(dcid); - dcArtistCache.AddOrUpdate(dcid, _ => dc_artist, (_, _) => dc_artist); - } - - // Get URL for requested image - _logger.LogDebug("getting discogs artist images"); - var images = dc_artist.Value("images"); - if (images != null && images.Count > idB) - { - _logger.LogDebug("got discogs artist image, sending"); - var thisImage = images[idB]; - imageUrl = thisImage.Value("uri"); - } - } - if (string.IsNullOrEmpty(imageUrl)) - { - try - { - _logger.LogDebug("Trying discogs artistID"); - (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(id); - if (dc_artist != null) - imageUrl = dc_artist["images"].First(i => i.Value("type") == "primary").Value("uri"); - } - catch - { - _logger.LogDebug("using coverart archive"); - imageUrl = $"https://coverartarchive.org/release/{id}/front"; - } - } - - var imgResponse = await imageUrl.GetAsync(); - if (imgResponse.StatusCode != 200) - { - _logger.LogDebug("failed to fetch image"); - return StatusCode(imgResponse.StatusCode); - } - - var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); - if (resize && image.Size.Width > width) - { - _logger.LogDebug("resizing"); - image.Mutate(x => x.Resize(width, 0)); - } - - using var stream = new MemoryStream(); - - if (contenttype.Contains("jpeg")) - { - _logger.LogDebug("sending as jpg"); - image.Save(stream, new JpegEncoder()); - } - else if (contenttype.Contains("bmp")) - { - _logger.LogDebug("bmp"); - image.Save(stream, new BmpEncoder()); - } - else if (contenttype.Contains("png")) - { - _logger.LogDebug("sending as png"); - image.Save(stream, new PngEncoder()); - } - - return File(stream.ToArray(), contenttype); - } - - //width=480&resize=true&contenttype=image/jpeg - [HttpGet("music/artist/{mbid}/deviceBackgroundImage")] - public async Task PrimaryImage(Guid mbid, string contenttype = "image/jpeg", int width = 480, bool resize = true) - { - _logger.LogDebug("getting by mbid"); - (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); - if (dc_artist == null) - { - _logger.LogDebug("failed to fetch by mbid"); - return StatusCode(404); - } - - _logger.LogDebug("getting primary image"); - string imgUrl = dc_artist["images"].First(i => i.Value("type") == "primary").Value("uri"); - var imgResponse = await imgUrl.GetAsync(); - if (imgResponse.StatusCode != 200) - { - _logger.LogDebug("failed to fetch image uri"); - return StatusCode(imgResponse.StatusCode); - } - - var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); - if (resize && image.Size.Width > width) - { - _logger.LogDebug("resizing"); - image.Mutate(x => x.Resize(width, 0)); - } - - using var stream = new MemoryStream(); - - if (contenttype.Contains("jpeg")) - { - _logger.LogDebug("sending as jpg"); - image.Save(stream, new JpegEncoder()); - } - else if (contenttype.Contains("bmp")) - { - _logger.LogDebug("bmp"); - image.Save(stream, new BmpEncoder()); - } - else if (contenttype.Contains("png")) - { - _logger.LogDebug("sending as png"); - image.Save(stream, new PngEncoder()); - } - - return File(stream.ToArray(), contenttype); - } - } -} \ No newline at end of file diff --git a/Zune.Net.Catalog.Image/Program.cs b/Zune.Net.Catalog.Image/Program.cs index ea343a7..772216c 100644 --- a/Zune.Net.Catalog.Image/Program.cs +++ b/Zune.Net.Catalog.Image/Program.cs @@ -26,7 +26,7 @@ public static void Main(string[] args) var app = builder.Build(); - app.UseHttpLogging(); + // app.UseHttpLogging(); // Configure the HTTP request pipeline. diff --git a/Zune.Net.Catalog.Image/appsettings.Development.json b/Zune.Net.Catalog.Image/appsettings.Development.json index dc23838..8090d9a 100644 --- a/Zune.Net.Catalog.Image/appsettings.Development.json +++ b/Zune.Net.Catalog.Image/appsettings.Development.json @@ -1,7 +1,11 @@ { + "ZuneNetContext": { + "ConnectionString": "mongodb://root:rootpassword@localhost:27017", + "DatabaseName": "Zune" + }, "Logging": { "LogLevel": { "Default": "Debug" } } -} +} \ No newline at end of file diff --git a/Zune.Net.Catalog/Program.cs b/Zune.Net.Catalog/Program.cs index c88bdd8..b89c328 100644 --- a/Zune.Net.Catalog/Program.cs +++ b/Zune.Net.Catalog/Program.cs @@ -1,11 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; namespace Zune.Net.Catalog { diff --git a/Zune.Net.Commerce/Program.cs b/Zune.Net.Commerce/Program.cs index 5916c4e..b042c01 100644 --- a/Zune.Net.Commerce/Program.cs +++ b/Zune.Net.Commerce/Program.cs @@ -27,7 +27,7 @@ public static void Main(string[] args) var app = builder.Build(); - app.UseHttpLogging(); + // app.UseHttpLogging(); // Configure the HTTP request pipeline. app.UseRequestBuffering(); diff --git a/Zune.Net.Commerce/Properties/launchSettings.json b/Zune.Net.Commerce/Properties/launchSettings.json deleted file mode 100644 index afefa89..0000000 --- a/Zune.Net.Commerce/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:80", - "sslPort": 443 - } - }, - "$schema": "http://json.schemastore.org/launchsettings.json", - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Zune.Net.Commerce": { - "commandName": "Project", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "dotnetRunMessages": "true", - "applicationUrl": "https://127.0.0.3:443;http://127.0.0.3:80" - } - } -} \ No newline at end of file diff --git a/Zune.Net.Inbox/Controllers/MessagingInboxController.cs b/Zune.Net.Inbox/Controllers/MessagingInboxController.cs index b14f1a9..c8065f1 100644 --- a/Zune.Net.Inbox/Controllers/MessagingInboxController.cs +++ b/Zune.Net.Inbox/Controllers/MessagingInboxController.cs @@ -1,15 +1,24 @@ using Atom.Xml; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using System; using Zune.Xml.Inbox; namespace Zune.Net.Inbox.Controllers { + ///en-US/messaging/xerootg/inbox/unreadcount [ApiController] [Route("/{locale}/messaging/{zuneTag}/inbox/{action=List}/{id?}")] + [Route("/{locale}/messaging/{zuneTag}/inbox/")] [Route("/messaging/{zuneTag}/inbox/{action=List}/{id?}")] public class MessagingInboxController : ControllerBase { + private ILogger _logger; + public MessagingInboxController(ILogger logger) + { + _logger = logger; + } + [HttpGet] public Feed List(string locale, string zuneTag) { @@ -57,17 +66,16 @@ public ActionResult Details(string locale, string zuneTag, strin } [HttpGet("unreadcount")] - [HttpGet("UnreadCount")] - public IActionResult UnreadCont(string locale, string zuneTag) + public IActionResult UnreadCont(string zuneTag) { - + _logger.LogInformation($"{zuneTag} message count requested"); // using var ctx = new ZuneNetContext(); // Member member = ctx.Members.First(m => m.ZuneTag == zuneTag); // if (member == null) // return StatusCode(StatusCodes.Status400BadRequest, $"User {zuneTag} does not exist."); // return Content(member.Messages.Count(msg => !msg.IsRead).ToString()); - return Ok("1"); + return Ok("1"); } } } diff --git a/Zune.Net.Inbox/Program.cs b/Zune.Net.Inbox/Program.cs index 1834ea1..812e49c 100644 --- a/Zune.Net.Inbox/Program.cs +++ b/Zune.Net.Inbox/Program.cs @@ -1,26 +1,18 @@ -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; +using Zune.Net; -namespace Zune.Net.Inbox -{ - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } +var builder = WebApplication.CreateBuilder(args); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }) - .ConfigureServices((ctx, s) => - { - s.Configure(ctx.Configuration.GetSection("ZuneNetContext")); - s.AddSingleton(); - }); - } -} +// Add services to the container. + +builder.Services.AddControllers().AddXmlSerializerFormatters(); +builder.Host.ConfigureZuneDB(true); + +var app = builder.Build(); + +// app.UseHttpLogging(); + +app.MapControllers(); + +app.Run(); diff --git a/Zune.Net.Inbox/Properties/launchSettings.json b/Zune.Net.Inbox/Properties/launchSettings.json deleted file mode 100644 index ed72345..0000000 --- a/Zune.Net.Inbox/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:80", - "sslPort": 443 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Zune.Net.Inbox": { - "commandName": "Project", - "dotnetRunMessages": "true", - "applicationUrl": "https://127.0.0.7:443;http://127.0.0.7:80", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/Zune.Net.Inbox/Startup.cs b/Zune.Net.Inbox/Startup.cs index 05e5892..ca6e8fa 100644 --- a/Zune.Net.Inbox/Startup.cs +++ b/Zune.Net.Inbox/Startup.cs @@ -1,15 +1,8 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Zune.Net.Middleware; namespace Zune.Net.Inbox @@ -37,7 +30,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseDeveloperExceptionPage(); } - app.UseHttpLogging(); + // app.UseHttpLogging(); app.UseRouting(); diff --git a/Zune.Net.Login/Program.cs b/Zune.Net.Login/Program.cs index 3e82cf2..018b04e 100644 --- a/Zune.Net.Login/Program.cs +++ b/Zune.Net.Login/Program.cs @@ -17,7 +17,7 @@ public static void Main(string[] args) builder.Services.AddControllers(); var app = builder.Build(); - app.UseHttpLogging(); + // app.UseHttpLogging(); // Configure the HTTP request pipeline. diff --git a/Zune.Net.MetaServices/Program.cs b/Zune.Net.MetaServices/Program.cs index 913cb35..8490d5d 100644 --- a/Zune.Net.MetaServices/Program.cs +++ b/Zune.Net.MetaServices/Program.cs @@ -17,7 +17,7 @@ var app = builder.Build(); -app.UseHttpLogging(); +// app.UseHttpLogging(); app.MapControllers(); app.UseResponseCaching(); diff --git a/Zune.Net.Mix/Program.cs b/Zune.Net.Mix/Program.cs index 7928abe..5ae8dd6 100644 --- a/Zune.Net.Mix/Program.cs +++ b/Zune.Net.Mix/Program.cs @@ -12,7 +12,7 @@ // Configure the HTTP request pipeline. -app.UseHttpLogging(); +// app.UseHttpLogging(); app.UseAuthorization(); diff --git a/Zune.Net.SocialApi/Controllers/MembersController.cs b/Zune.Net.SocialApi/Controllers/MembersController.cs index 2e96e2a..bb947ac 100644 --- a/Zune.Net.SocialApi/Controllers/MembersController.cs +++ b/Zune.Net.SocialApi/Controllers/MembersController.cs @@ -95,13 +95,14 @@ public async Task>> Badges(string zuneTag) var feed = new Feed { - Id = Guid.Empty.ToString(), + Id = new Guid().ToString(), Links = { new Link(requestUrl) }, Title = member.ZuneTag + "'s Badges", - Entries = - { - badge1 - } + Updated = DateTime.Now - TimeSpan.FromDays(1), + // Entries = + // { + // badge1 + // } }; return feed; diff --git a/Zune.Net.SocialApi/Controllers/WebStatsController.cs b/Zune.Net.SocialApi/Controllers/WebStatsController.cs new file mode 100644 index 0000000..2dacfb7 --- /dev/null +++ b/Zune.Net.SocialApi/Controllers/WebStatsController.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using System.IO; + +namespace Zune.Net.SocialApi.Controllers +{ + [ApiController] + public class WebStatsController : ControllerBase + { + private readonly IWebHostEnvironment _env; + + public WebStatsController(IWebHostEnvironment env) + { + _env = env; + } + + // sometimes things crash when m.webtrends.net doesnt return a valid gif, for example m.webtrends.net/something/something.gif&abunch=of&args=that... + + [Route("/{gibberish}/{file}.gif")] + public ActionResult LiveTOU() + { + string path = Path.Combine(_env.ContentRootPath, "Resources", "1x1px.gif"); + var image = System.IO.File.ReadAllBytes(path); + + return File(image, "image/gif"); + } + } +} diff --git a/Zune.Net.SocialApi/Properties/launchSettings.json b/Zune.Net.SocialApi/Properties/launchSettings.json deleted file mode 100644 index d445250..0000000 --- a/Zune.Net.SocialApi/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://127.0.0.5:80", - "sslPort": 443 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Zune.Net.SocialApi": { - "commandName": "Project", - "dotnetRunMessages": true, - "applicationUrl": "https://127.0.0.5:443;http://127.0.0.5:80;https://127.0.0.13:443;http://127.0.0.13:80", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/Zune.Net.SocialApi/Resources/1x1px.gif b/Zune.Net.SocialApi/Resources/1x1px.gif new file mode 100644 index 0000000000000000000000000000000000000000..3c828914c1f3e7729ea12f60aada1bc2d5402c13 GIT binary patch literal 842 zcmV-Q1GW4|Nk%w1VF3UE0QUd@000010RaL60s{jB1Ox;H1qB8M1_uWR2nYxX2?+`c z3JVJh3=9kn4Gj(s4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1M=dJ1KqU-CI?d^>3?}730ck}aZ^z>x)^;`G% zQ~3Bw`T0Zo`aApkH2eE5{QM{U{38AR9R2+l{{9pG{ty2C3;zBH{{9C3{saF00{;F0 z{{8^|{s8~~A^8Le00930A^smnWo~71VRU6=AYyqSZ*FX9Ze>SFMs#m)Y;!JSZ*2f9 U000000RRC200;yC{{;j9JBsIi!2kdN literal 0 HcmV?d00001 diff --git a/Zune.Net.SocialApi/Startup.cs b/Zune.Net.SocialApi/Startup.cs index 5ebc78f..f13bef9 100644 --- a/Zune.Net.SocialApi/Startup.cs +++ b/Zune.Net.SocialApi/Startup.cs @@ -39,7 +39,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseDeveloperExceptionPage(); } - app.UseHttpLogging(); + // app.UseHttpLogging(); app.UseRequestBuffering(); diff --git a/Zune.Net.SocialApi/Zune.Net.SocialApi.csproj b/Zune.Net.SocialApi/Zune.Net.SocialApi.csproj index f7ae83d..87c7c7a 100644 --- a/Zune.Net.SocialApi/Zune.Net.SocialApi.csproj +++ b/Zune.Net.SocialApi/Zune.Net.SocialApi.csproj @@ -4,6 +4,13 @@ net7.0 + + + PreserveNewest + Always + + + @@ -11,4 +18,4 @@ - + \ No newline at end of file diff --git a/Zune.Net.Tiles/Program.cs b/Zune.Net.Tiles/Program.cs index 335b244..4456171 100644 --- a/Zune.Net.Tiles/Program.cs +++ b/Zune.Net.Tiles/Program.cs @@ -6,12 +6,10 @@ var app = builder.Build(); -app.UseHttpLogging(); +// app.UseHttpLogging(); // Configure the HTTP request pipeline. -app.UseHttpsRedirection(); - app.UseAuthorization(); app.MapControllers(); diff --git a/Zune.Net.Tuners/Program.cs b/Zune.Net.Tuners/Program.cs index 38e7efc..260906f 100644 --- a/Zune.Net.Tuners/Program.cs +++ b/Zune.Net.Tuners/Program.cs @@ -4,7 +4,7 @@ var app = builder.Build(); -app.UseHttpLogging(); +// app.UseHttpLogging(); app.MapGet("/{locale}/ZunePCClient/{version}/{file}.xml", (string locale, string version, string file) => { diff --git a/runProd.sh b/runProd.sh index aaaa8d7..5b982c5 100755 --- a/runProd.sh +++ b/runProd.sh @@ -2,3 +2,4 @@ docker compose stop docker compose build docker compose up -d --remove-orphans +docker compose logs --follow --tail 0 From 06accc0646c7d53fae39c0fd6a06bb170b8515c8 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Mon, 5 Jun 2023 22:23:25 -0600 Subject: [PATCH 24/29] - Consolidation of HD specific catalog endpoints - actual image resizing - MIX should now be returning something sensible --- .editorconfig | 4 + Zune.DB/Zune.DB.csproj | 3 +- .../Controllers/ImageController.cs | 6 +- Zune.Net.Catalog.Image/Program.cs | 5 +- .../Controllers/Music/AlbumController.cs | 2 +- .../Controllers/Music/ArtistController.cs | 124 +- .../Controllers/Music/ChartController.cs | 37 +- .../Controllers/Music/FeaturesController.cs | 2 +- .../Controllers/Music/GenreController.cs | 10 +- .../Controllers/Music/TrackController.cs | 6 +- .../Music/ZuneHDArtistController.cs | 175 -- .../Controllers/Podcast/ChartController.cs | 2 +- .../Controllers/Podcast/PodcastController.cs | 6 +- .../Controllers/StreamController.cs | 2 +- Zune.Net.Catalog/Startup.cs | 2 +- Zune.Net.Catalog/Zune.Net.Catalog.csproj | 1 + Zune.Net.Catalog/appsettings.json | 4 +- Zune.Net.Inbox/appsettings.json | 2 - Zune.Net.Login/appsettings.json | 3 +- Zune.Net.MetaServices/Controllers/FAI.cs | 43 +- .../DomainModels/Endpoints/Endpoint.cs | 4 +- .../DomainModels/Endpoints/Endpoints.cs | 2 +- .../DomainModels/MDAR-CD/MdarCd.cs | 37 +- .../MDAR-CD/MdarCdRequestMetadata.cs | 4 +- .../DomainModels/MDAR-CD/MdarTrack.cs | 8 +- .../DomainModels/MDQ-CD/MdqAlbum.cs | 4 +- .../DomainModels/MDQ-CD/MdqCd.cs | 6 +- .../MDQ-CD/MdqDescriptionElement.cs | 4 +- .../DomainModels/MDQ-CD/MdqRequestMetadata.cs | 2 +- .../DomainModels/MDQ-CD/MdqTrack.cs | 14 +- .../DomainModels/MDR-CD/MdrCd.cs | 24 +- .../DomainModels/MDR-CD/MdrRequestMetadata.cs | 8 +- .../DomainModels/MDSR-CD/MdsrAlbum.cs | 10 +- .../MDSR-CD/MdsrAlbumRequestMetadata.cs | 2 +- .../MDSR-CD/MdsrAlbumSearchResult.cs | 2 +- Zune.Net.MetaServices/Models/WMIS.cs | 4 +- Zune.Net.MetaServices/appsettings.json | 1 - Zune.Net.Mix/Controllers/AlbumController.cs | 45 + Zune.Net.Mix/Controllers/ArtistController.cs | 27 +- Zune.Net.Mix/Controllers/TrackController.cs | 6 +- Zune.Net.Mix/DomainModel/AlbumModel.cs | 13 + Zune.Net.Mix/DomainModel/VectorEntry.cs | 35 + Zune.Net.Mix/appsettings.json | 1 - Zune.Net.Shared/Helpers/MusicBrainz.Album.cs | 60 +- Zune.Net.Shared/Helpers/MusicBrainz.Artist.cs | 18 + .../Helpers/MusicBrainzGenreList.cs | 1794 +++++++++++++++++ Zune.Net.SocialApi/appsettings.json | 2 - Zune.Net.Tiles/appsettings.json | 1 - Zune.Net.Tuners/appsettings.json | 3 +- 49 files changed, 2197 insertions(+), 383 deletions(-) create mode 100644 .editorconfig delete mode 100644 Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs create mode 100644 Zune.Net.Mix/Controllers/AlbumController.cs create mode 100644 Zune.Net.Mix/DomainModel/AlbumModel.cs create mode 100644 Zune.Net.Mix/DomainModel/VectorEntry.cs create mode 100644 Zune.Net.Shared/Helpers/MusicBrainzGenreList.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b8f4227 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ + +# Format: rule = severity # defintion; justification +dotnet_diagnostic.ASP0014.severity = none # Don't use UseEndpoints in trivial APIs; we have some simplistic program.cs, and still need to manually register UseEndpoints +dotnet_diagnostic.CA2254.severity = none # use logging formatter instead of interpolation; formatted strings in logs give me the creeps - also, too many nights oncall sorting impossible to read logs diff --git a/Zune.DB/Zune.DB.csproj b/Zune.DB/Zune.DB.csproj index 8aa2900..dc70449 100644 --- a/Zune.DB/Zune.DB.csproj +++ b/Zune.DB/Zune.DB.csproj @@ -1,7 +1,8 @@  - net6.0 + net7.0 + enable diff --git a/Zune.Net.Catalog.Image/Controllers/ImageController.cs b/Zune.Net.Catalog.Image/Controllers/ImageController.cs index 927f90b..bf0d068 100644 --- a/Zune.Net.Catalog.Image/Controllers/ImageController.cs +++ b/Zune.Net.Catalog.Image/Controllers/ImageController.cs @@ -33,7 +33,7 @@ public ImageController(ZuneNetContext database, ILogger logger) _logger = logger; } - [HttpGet, Route("image/{id}")] + [HttpGet("image/{id}")] public async Task Image(Guid id, bool resize = true, int width = 480, string contenttype = "image/jpeg") { string? imageUrl = null; @@ -88,7 +88,7 @@ public async Task Image(Guid id, bool resize = true, int width = // i.e. http://image.catalog.zune.net/v3.0/en-US/music/track/f32bb0ab-59d6-4620-b239-e86dc68647a4/albumImage?width=240&height=240&resize=true - [HttpGet, Route("music/{imageKind}/{id}/{type}")] + [HttpGet("music/{imageKind}/{id}/{type}")] public async Task ArtistImage(Guid id, string type, bool resize = true, int width = 480, string contenttype = "image/jpeg") { _logger.LogDebug($"Fetching image type: '{type}', starting with DC"); @@ -154,7 +154,7 @@ private static async Task GetImageUrlFromDCAsync(Guid id) return dc_artist.Value("images")? .FirstOrDefault(i => i.Value("type") == "primary")? - .Value("uri"); + .Value("uri") ?? throw new FileNotFoundException(); } private static string GetImageUrlFromCoverArchive(Guid id) diff --git a/Zune.Net.Catalog.Image/Program.cs b/Zune.Net.Catalog.Image/Program.cs index 772216c..71e3191 100644 --- a/Zune.Net.Catalog.Image/Program.cs +++ b/Zune.Net.Catalog.Image/Program.cs @@ -13,10 +13,7 @@ public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); - builder.Host.ConfigureLogging(cfg => - { - cfg.AddConsole(); - }); + builder.Logging.AddConsole(); // Add services to the container. diff --git a/Zune.Net.Catalog/Controllers/Music/AlbumController.cs b/Zune.Net.Catalog/Controllers/Music/AlbumController.cs index 6531373..d958ea3 100644 --- a/Zune.Net.Catalog/Controllers/Music/AlbumController.cs +++ b/Zune.Net.Catalog/Controllers/Music/AlbumController.cs @@ -12,7 +12,7 @@ namespace Zune.Net.Catalog.Controllers.Music public class AlbumController : Controller { - [HttpGet, Route("")] + [HttpGet("")] public ActionResult> Search() { if (!Request.Query.TryGetValue("q", out var queries) || queries.Count != 1) diff --git a/Zune.Net.Catalog/Controllers/Music/ArtistController.cs b/Zune.Net.Catalog/Controllers/Music/ArtistController.cs index 7a5810c..6e956fc 100644 --- a/Zune.Net.Catalog/Controllers/Music/ArtistController.cs +++ b/Zune.Net.Catalog/Controllers/Music/ArtistController.cs @@ -1,13 +1,10 @@ using Atom.Xml; using Flurl.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using System; -using System.Collections.Generic; using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; using System.Threading.Tasks; using Zune.DB; using Zune.Net.Helpers; @@ -16,25 +13,30 @@ namespace Zune.Net.Catalog.Controllers.Music { [Route("/v3.2/{culture}/music/artist/")] + [Route("/v3.0/{culture}/music/artist/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ArtistController : Controller { private readonly ZuneNetContext _database; - public ArtistController(ZuneNetContext database) + private readonly ILogger _logger; + + public ArtistController(ILogger logger, ZuneNetContext database) { + _logger = logger; _database = database; } - [HttpGet, Route("")] - public ActionResult> Search() + [HttpGet("")] + public ActionResult> Search(string q) { - if (!Request.Query.TryGetValue("q", out var queries) || queries.Count != 1) + if(string.IsNullOrEmpty(q)) + { return BadRequest(); - - return MusicBrainz.SearchArtists(queries[0], Request.Path); + } + return MusicBrainz.SearchArtists(q, Request.Path); } - [HttpGet, Route("{mbid}")] + [HttpGet("{mbid}")] public async Task> Details(Guid mbid) { (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); @@ -62,55 +64,44 @@ public async Task> Details(Guid mbid) return artist; } - [HttpGet, Route("{mbid}/tracks")] - public ActionResult> Tracks(Guid mbid) - { - if (!Request.Query.TryGetValue("chunkSize", out var chunkSizeStrs) || chunkSizeStrs.Count != 1) - return BadRequest(); - - return MusicBrainz.GetArtistTracksByMBID(mbid, Request.Path, int.Parse(chunkSizeStrs[0])); - } - - [HttpGet, Route("{mbid}/albums")] - public ActionResult> Albums(Guid mbid) + [HttpGet("{mbid}/tracks")] + public ActionResult> Tracks(Guid mbid, string orderby = "unset", int chunkSize = 10) { - var feed = MusicBrainz.GetArtistAlbumsByMBID(mbid, Request.Path); - - Comparison sortComparer = (a, b) => a.ReleaseDate.Year.CompareTo(b.ReleaseDate.Year); - if (Request.Query.TryGetValue("orderby", out var orderByValue)) + var tracks = MusicBrainz.GetArtistTracksByMBID(mbid, Request.Path, 100); + var toSort = tracks.Entries; + try { - string orderBy = orderByValue.Single().ToLower(); - switch (orderBy) + switch (orderby.ToLowerInvariant()) { - case "title": - sortComparer = (a, b) => (a.SortTitle ?? a.Title.Value).CompareTo(b.SortTitle ?? b.Title.Value); - break; - - case "mostplayed": - sortComparer = (a, b) => a.Popularity.CompareTo(b.Popularity); + case "playrank": + tracks.Entries.Sort((a, b) => a.Popularity.CompareTo(b)); break; } } - - feed.Entries.Sort(sortComparer); - return feed; + catch { } + tracks.Entries = tracks.Entries.Take(chunkSize).ToList(); + return tracks; } - [HttpGet, Route("{mbid}/primaryImage")] - public async Task PrimaryImage(Guid mbid) + [HttpGet("{mbid}/albums")] + public ActionResult> Albums(Guid mbid, int chunkSize = 10, string orderby = "ReleaseDate") { - (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); - if (dc_artist == null) - return StatusCode(404); + var feed = MusicBrainz.GetArtistAlbumsByMBID(mbid, Request.Path); + Comparison sortComparer = orderby.ToLowerInvariant() switch + { + "title" => (a, b) => (a.SortTitle ?? a.Title.Value).CompareTo(b.SortTitle ?? b.Title.Value), - string imgUrl = dc_artist["images"].First(i => i.Value("type") == "primary").Value("uri"); - var imgResponse = await imgUrl.GetAsync(); - if (imgResponse.StatusCode != 200) - return StatusCode(imgResponse.StatusCode); - return File(await imgResponse.GetStreamAsync(), "image/jpeg"); + "mostplayed" or "playrank" => (a, b) => a.Popularity.CompareTo(b.Popularity), + + //default + _ or "releasedate" => (a, b) => a.ReleaseDate.Year.CompareTo(b.ReleaseDate.Year), + }; + feed.Entries.Sort(sortComparer); + feed.Entries = feed.Entries.Take(chunkSize).ToList(); + return feed; } - [HttpGet, Route("{mbid}/biography")] + [HttpGet("{mbid}/biography")] public async Task> Biography(Guid mbid) { (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); @@ -127,12 +118,33 @@ public async Task> Biography(Guid mbid) Updated = updated, }; } + + [HttpGet("{mbid}/primaryImage")] + public async Task PrimaryImage(Guid mbid) + { + (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); + if (dc_artist == null) + { + _logger.LogDebug($"No artist found for mbid: {mbid}"); + return StatusCode(404); + } + + string imgUrl = dc_artist["images"].First(i => i.Value("type") == "primary").Value("uri"); + var imgResponse = await imgUrl.GetAsync(); + if (imgResponse.StatusCode != 200) + return StatusCode(imgResponse.StatusCode); + return File(await imgResponse.GetStreamAsync(), "image/jpeg"); + } - [HttpGet, Route("{mbid}/images")] - public async Task>> Images(Guid mbid) + [HttpGet("{mbid}/images")] + public async Task>> Images(Guid mbid, int chunkSize = 40) { (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); DateTime updated = DateTime.Now; + if(dc_artist == null) + { + return StatusCode(404); + } int dcid = dc_artist.Value("id"); byte[] zero = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -167,17 +179,19 @@ public async Task>> Images(Guid mbid) } } }; - }).ToList(); + }).Take(chunkSize).ToList(); } return feed; } - [HttpGet, Route("{mbid}/similarArtists")] - [HttpGet, Route("{mbid}/influencers")] - public async Task>> SimilarArtists(Guid mbid) + [HttpGet("{mbid}/similarArtists")] + [HttpGet("{mbid}/influencers")] + public async Task>> SimilarArtists(Guid mbid, int chunkSize = 10) { - return await LastFM.GetSimilarArtistsByMBID(mbid); + var similar = await LastFM.GetSimilarArtistsByMBID(mbid); + similar.Entries = similar.Entries.Take(chunkSize).ToList(); + return similar; } } } diff --git a/Zune.Net.Catalog/Controllers/Music/ChartController.cs b/Zune.Net.Catalog/Controllers/Music/ChartController.cs index 79c4319..6db58bc 100644 --- a/Zune.Net.Catalog/Controllers/Music/ChartController.cs +++ b/Zune.Net.Catalog/Controllers/Music/ChartController.cs @@ -13,15 +13,12 @@ namespace Zune.Net.Catalog.Controllers.Music [Produces(Atom.Constants.ATOM_MIMETYPE)] public class ChartController : Controller { - private const bool useDeezer = true; - - [HttpGet, Route("tracks")] + [HttpGet("tracks")] public async Task>> Tracks() { Feed feed; - if (useDeezer) - { +#if UseDeezer var dz_tracks = await Deezer.GetChartDZTracks(); DateTime updated = DateTime.Now; @@ -45,30 +42,28 @@ public async Task>> Tracks() feed.Entries.Add(track); } - } - else - { - var fm_tracks = await LastFM.GetTopTracks(); - feed = LastFM.CreateFeed("/music/chart/zune/tracks", "Top tracks"); +#else + var fm_tracks = await LastFM.GetTopTracks(); + feed = LastFM.CreateFeed("/music/chart/zune/tracks", "Top tracks"); - foreach (var fm_track in fm_tracks.Take(10)) - { - var mb_recording = LastFM.GetMBRecordingByFMTrack(fm_track); - if (mb_recording == null) - continue; + foreach (var fm_track in fm_tracks.Take(10)) + { + var mb_recording = LastFM.GetMBRecordingByFMTrack(fm_track); + if (mb_recording == null) + continue; - var track = MusicBrainz.MBRecordingToTrack(mb_recording, updated: feed.Updated, includeRights: true); - track.Popularity = fm_track.Rank ?? 0; - track.PlayCount = fm_track.PlayCount ?? 0; + var track = MusicBrainz.MBRecordingToTrack(mb_recording, updated: feed.Updated, includeRights: true); + track.Popularity = fm_track.Rank ?? 0; + track.PlayCount = fm_track.PlayCount ?? 0; - feed.Entries.Add(track); - } + feed.Entries.Add(track); } +#endif return feed; } - [HttpGet, Route("albums")] + [HttpGet("albums")] public async Task>> Albums() { var dz_albums = await Deezer.GetChartDZAlbums(); diff --git a/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs b/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs index 28a85bc..e89c42e 100644 --- a/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs +++ b/Zune.Net.Catalog/Controllers/Music/FeaturesController.cs @@ -11,7 +11,7 @@ namespace Zune.Net.Catalog.Controllers.Music [Produces(Atom.Constants.ATOM_MIMETYPE)] public class FeaturesController : Controller { - [HttpGet, Route("")] + [HttpGet("")] public ActionResult> Features() { var updated = DateTime.Now; diff --git a/Zune.Net.Catalog/Controllers/Music/GenreController.cs b/Zune.Net.Catalog/Controllers/Music/GenreController.cs index bbfd97c..2f6a5e7 100644 --- a/Zune.Net.Catalog/Controllers/Music/GenreController.cs +++ b/Zune.Net.Catalog/Controllers/Music/GenreController.cs @@ -12,13 +12,13 @@ namespace Zune.Net.Catalog.Controllers.Music public class GenreController : Controller { - [HttpGet, Route("")] + [HttpGet("")] public ActionResult> Genres() { return MusicBrainz.GetGenres(Request.Path); } - [HttpGet, Route("{id}")] + [HttpGet("{id}")] public ActionResult Details(string id) { if (Guid.TryParse(id, out Guid mbid)) @@ -26,7 +26,7 @@ public ActionResult Details(string id) return MusicBrainz.GetGenreByZID(id); } - [HttpGet, Route("{id}/albums")] + [HttpGet("{id}/albums")] public ActionResult> Albums(string id) { if (Guid.TryParse(id, out Guid mbid)) @@ -35,7 +35,7 @@ public ActionResult> Albums(string id) } // Not actually used by Zune, but hey, might as well - [HttpGet, Route("{id}/tracks")] + [HttpGet("{id}/tracks")] public ActionResult> Tracks(string id) { if (Guid.TryParse(id, out Guid mbid)) @@ -43,7 +43,7 @@ public ActionResult> Tracks(string id) return MusicBrainz.GetGenreTracksByZID(id, Request.Path); } - [HttpGet, Route("{id}/artists")] + [HttpGet("{id}/artists")] public ActionResult> Artists(string id) { if (Guid.TryParse(id, out Guid mbid)) diff --git a/Zune.Net.Catalog/Controllers/Music/TrackController.cs b/Zune.Net.Catalog/Controllers/Music/TrackController.cs index 3763ea5..c96e369 100644 --- a/Zune.Net.Catalog/Controllers/Music/TrackController.cs +++ b/Zune.Net.Catalog/Controllers/Music/TrackController.cs @@ -21,7 +21,7 @@ public TrackController(IWebHostEnvironment env) _env = env; } - [HttpGet, Route("")] + [HttpGet("")] public ActionResult> Search() { if (!Request.Query.TryGetValue("q", out var queries) || queries.Count != 1) @@ -30,13 +30,13 @@ public ActionResult> Search() return MusicBrainz.SearchTracks(queries[0], Request.Path); } - [HttpGet, Route("{mbid}")] + [HttpGet("{mbid}")] public ActionResult Details(Guid mbid) { return MusicBrainz.GetTrackByMBID(mbid); } - [HttpGet, Route("{mbid}/similarTracks")] + [HttpGet("{mbid}/similarTracks")] public async Task>> SimilarTracks(Guid mbid) { return await LastFM.GetSimilarTracksByMBID(mbid); diff --git a/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs b/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs deleted file mode 100644 index 23535ab..0000000 --- a/Zune.Net.Catalog/Controllers/Music/ZuneHDArtistController.cs +++ /dev/null @@ -1,175 +0,0 @@ -using Atom.Xml; -using Flurl.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json.Linq; -using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.Processing; -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Zune.DB; -using Zune.Net.Helpers; -using Zune.Xml.Catalog; - -namespace Zune.Net.Catalog.Controllers.Music -{ - [Route("/v3.0/{culture}/music/artist/")] - [Produces(Atom.Constants.ATOM_MIMETYPE)] - public class ZuneHDArtistController : Controller - { - private readonly ZuneNetContext _database; - private readonly ILogger _logger; - public ZuneHDArtistController(ILogger logger, ZuneNetContext database) - { - _logger = logger; - _database = database; - } - - [HttpGet, Route("{mbid}")] - public async Task> Details(Guid mbid) - { - (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); - Artist artist = MusicBrainz.MBArtistToArtist(mb_artist); - artist.Images = new() { new() { Id = mbid } }; - - if (dc_artist != null) - { - artist.Biography = dc_artist.Value("profile"); - artist.Links.Add(new(Request.Path.Value + "biography", relation: "zune://artist/biography")); - - var dc_artist_image = dc_artist.Value("images")?.FirstOrDefault(i => i.Value("type") == "primary"); - if (dc_artist_image != null) - { - string artistImageUrl = dc_artist_image.Value("uri"); - var artistImageEntry = await _database.AddImageAsync(artistImageUrl); - - artist.BackgroundImage = new() - { - Id = artistImageEntry.Id - }; - } - } - - return artist; - } - - // orderby ReleaseDate|PlayRank optional chunkSize={int} - [HttpGet, Route("{mbid}/tracks")] - public ActionResult> Tracks(Guid mbid, string orderby = "unset", int chunkSize = 10) - { - var tracks = MusicBrainz.GetArtistTracksByMBID(mbid, Request.Path, 100); - var toSort = tracks.Entries; - try - { - switch (orderby.ToLowerInvariant()) - { - case "playrank": - tracks.Entries.Sort((a, b) => a.Popularity.CompareTo(b)); - break; - } - } - catch { } - tracks.Entries = tracks.Entries.Take(chunkSize).ToList(); - return tracks; - } - - // orderby ReleaseDate|PlayRank optional chunkSize={int} - [HttpGet, Route("{mbid}/albums")] - public ActionResult> Albums(Guid mbid, int chunkSize = 10, string orderby = "notset") - { - var feed = MusicBrainz.GetArtistAlbumsByMBID(mbid, Request.Path); - Comparison sortComparer = orderby.ToLowerInvariant() switch - { - "title" => (a, b) => (a.SortTitle ?? a.Title.Value).CompareTo(b.SortTitle ?? b.Title.Value), - - "mostplayed" or "playrank" => (a, b) => a.Popularity.CompareTo(b.Popularity), - //default - _ => (a, b) => a.ReleaseDate.Year.CompareTo(b.ReleaseDate.Year), - }; - feed.Entries.Sort(sortComparer); - feed.Entries = feed.Entries.Take(chunkSize).ToList(); - return feed; - } - - [HttpGet, Route("{mbid}/biography")] - public async Task> Biography(Guid mbid) - { - (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); - if (dc_artist == null) - return StatusCode(404); - DateTime updated = DateTime.Now; - - return new Entry - { - Id = $"tag:catalog.zune.net,1900-01-01:/music/artist/{mbid}/biography", - Title = mb_artist.Name, - Links = { new(Request.Path) }, - Content = Discogs.DCProfileToBiographyContent(dc_artist.Value("profile")), - Updated = updated, - }; - } - - //chunkSize=10 - [HttpGet, Route("{mbid}/images")] - public async Task>> Images(Guid mbid, int chunkSize) - { - (var dc_artist, var mb_artist) = await Discogs.GetDCArtistByMBID(mbid); - DateTime updated = DateTime.Now; - if(dc_artist == null) - { - return StatusCode(404); - } - int dcid = dc_artist.Value("id"); - byte[] zero = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; - - Feed feed = new() - { - Id = $"tag:catalog.zune.net,1900-01-01:/music/artist/{mbid}/images", - Title = mb_artist.Name, - Links = { new(Request.Path) }, - Updated = updated, - }; - - var images = dc_artist.Value("images"); - if (images != null) - { - feed.Entries = images.Select((j, idx) => - { - // Encode DCID and image index in ID - Guid imgId = new(dcid, (short)idx, 0, zero); - - return new Image - { - Id = imgId, - Instances = new() - { - new() - { - Id = imgId, - Url = j.Value("uri"), - Format = "jpg", - Width = j.Value("width"), - Height = j.Value("height"), - } - } - }; - }).Take(chunkSize).ToList(); - } - - return feed; - } - - //chunkSize=10 - [HttpGet, Route("{mbid}/similarArtists")] - public async Task>> SimilarArtists(Guid mbid, int chunkSize) - { - var similar = await LastFM.GetSimilarArtistsByMBID(mbid); - similar.Entries = similar.Entries.Take(chunkSize).ToList(); - return similar; - } - } -} diff --git a/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs b/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs index 9d2cb68..b3b593e 100644 --- a/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs +++ b/Zune.Net.Catalog/Controllers/Podcast/ChartController.cs @@ -17,7 +17,7 @@ public ChartController(ZuneNetContext database) _database = database; } - [HttpGet, Route("podcasts")] + [HttpGet("podcasts")] public async Task>> Podcasts(string culture) { int limit = 26; diff --git a/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs b/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs index 4364b81..4597173 100644 --- a/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs +++ b/Zune.Net.Catalog/Controllers/Podcast/PodcastController.cs @@ -20,7 +20,7 @@ public PodcastController(ZuneNetContext database) _database = database; } - [HttpGet, Route("podcast")] + [HttpGet("podcast")] public async Task>> Search() { if (!Request.Query.TryGetValue("q", out var queries) || queries.Count != 1) @@ -33,7 +33,7 @@ public async Task>> Search() return feed; } - [HttpGet, Route("podcast/{tdid}")] + [HttpGet("podcast/{tdid}")] public async Task Details(Guid tdid) { var podcast = await Taddy.GetPodcastByTDID(tdid); @@ -41,7 +41,7 @@ public async Task Details(Guid tdid) return podcast; } - [HttpGet, Route("podcastCategories")] + [HttpGet("podcastCategories")] public Feed Categories() { // TODO: Narrow the categories down to a few, not 110 diff --git a/Zune.Net.Catalog/Controllers/StreamController.cs b/Zune.Net.Catalog/Controllers/StreamController.cs index be5feaa..d5d281a 100644 --- a/Zune.Net.Catalog/Controllers/StreamController.cs +++ b/Zune.Net.Catalog/Controllers/StreamController.cs @@ -23,7 +23,7 @@ public StreamController(IWebHostEnvironment env) _env = env; } - [HttpGet, Route("music/{mbid}")] + [HttpGet("music/{mbid}")] public async Task DefaultStreaming(Guid mbid) { var track = MusicBrainz.GetTrackByMBID(mbid); diff --git a/Zune.Net.Catalog/Startup.cs b/Zune.Net.Catalog/Startup.cs index f8d959b..6d2a3f4 100644 --- a/Zune.Net.Catalog/Startup.cs +++ b/Zune.Net.Catalog/Startup.cs @@ -50,7 +50,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseDeveloperExceptionPage(); } - app.UseHttpLogging(); + //app.UseHttpLogging(); app.UseRequestBuffering(); diff --git a/Zune.Net.Catalog/Zune.Net.Catalog.csproj b/Zune.Net.Catalog/Zune.Net.Catalog.csproj index 69d4a97..fb100cf 100644 --- a/Zune.Net.Catalog/Zune.Net.Catalog.csproj +++ b/Zune.Net.Catalog/Zune.Net.Catalog.csproj @@ -3,6 +3,7 @@ net7.0 2a7e8fe8-2bbf-4b58-a184-68ae46722b1c + True diff --git a/Zune.Net.Catalog/appsettings.json b/Zune.Net.Catalog/appsettings.json index a7a8965..b7c0152 100644 --- a/Zune.Net.Catalog/appsettings.json +++ b/Zune.Net.Catalog/appsettings.json @@ -5,9 +5,7 @@ }, "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Default": "Information" } }, "AllowedHosts": "*" diff --git a/Zune.Net.Inbox/appsettings.json b/Zune.Net.Inbox/appsettings.json index 8383a8a..309b7d7 100644 --- a/Zune.Net.Inbox/appsettings.json +++ b/Zune.Net.Inbox/appsettings.json @@ -7,8 +7,6 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" diff --git a/Zune.Net.Login/appsettings.json b/Zune.Net.Login/appsettings.json index 78df620..734fda5 100644 --- a/Zune.Net.Login/appsettings.json +++ b/Zune.Net.Login/appsettings.json @@ -5,8 +5,7 @@ }, "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Default": "Information" } }, "AllowedHosts": "*" diff --git a/Zune.Net.MetaServices/Controllers/FAI.cs b/Zune.Net.MetaServices/Controllers/FAI.cs index 505bb89..8729660 100644 --- a/Zune.Net.MetaServices/Controllers/FAI.cs +++ b/Zune.Net.MetaServices/Controllers/FAI.cs @@ -17,27 +17,20 @@ public FAI(WMIS wmis, ILogger logger) } [HttpGet("Search")] [Produces("application/xml")] - public async Task Search(string SearchString, string resultTypeString, int maxNumberOfResults = 10) - { + public async Task Search(string SearchString, string resultTypeString, int maxNumberOfResults = 10) => // var results = await MusicBrainz.MDARSearchAlbums(SearchString); // //resultTypeString album or artist // return results[0]; - switch (resultTypeString) + resultTypeString switch { - case "album": //{WMISFAIAlbumsQuery}, List Album - // mdsr-cd - // UIX Type: AlbumList, of Album - return Ok(await _wmis.SearchAlbumsAsync(SearchString, maxNumberOfResults)); - case "artist": - // mdsr-cd, but with slightly different elements - // UIX Type: ArtistList, of Artist - return Ok("SUCCESS"); - case "track": - // WMISFAITracksQuery->TrackList (see WMISDATA.SCHEMA.XML) - return Ok(); - } - return NotFound(); - } + //{WMISFAIAlbumsQuery}, List Album + "album" => Ok(await _wmis.SearchAlbumsAsync(SearchString, maxNumberOfResults)),// mdsr-cd + // UIX Type: AlbumList, of Album + "artist" => Ok("SUCCESS"),// mdsr-cd, but with slightly different elements + // UIX Type: ArtistList, of Artist + "track" => Ok(),// WMISFAITracksQuery->TrackList (see WMISDATA.SCHEMA.XML) + _ => NotFound(), + }; // example - http://metaservices.zune.net/ZuneFAI/GetAlbumDetailsFromAlbumId?albumId=8144290952437462496&locale=1033&volume=1 // Also known as WMISFAIGetAlbumDetailsQuery in the UIX side @@ -45,12 +38,20 @@ public async Task Search(string SearchString, string resultTypeStr [HttpPost("GetAlbumDetailsFromAlbumId")] [HttpGet("GetAlbumDetailsFromAlbumId")] [Produces("application/xml")] - public async Task MDARGetAlbumDetailsFromAlbumId(Int64 albumId, int locale, string volume, [FromBody]MdqRequestMetadata request = null) + public async Task MDARGetAlbumDetailsFromAlbumId(Int64 albumId, int locale, string volume, [FromBody] MdqRequestMetadata? request = null) { - var response = await _wmis.GetMdarCdRequestFromInt64(albumId, volume, request); - if(request?.MdqCd?.MdqRequestId != null) + var response = await _wmis.GetMdarCdRequestFromInt64(albumId, volume, request!); + if (request?.MdqCd?.MdqRequestId != null) { - response.mdqRequestID = new Guid(request?.MdqCd?.MdqRequestId); + var mdqRequestId = request?.MdqCd?.MdqRequestId; + if (string.IsNullOrEmpty(mdqRequestId)) + { + response.mdqRequestID = new Guid(); + } + else + { + response.mdqRequestID = new Guid(mdqRequestId); + } } return Ok(response); } diff --git a/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoint.cs b/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoint.cs index e8a1190..251df90 100644 --- a/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoint.cs +++ b/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoint.cs @@ -2,7 +2,7 @@ namespace Zune.Net.MetaServices.DomainModels.Endpoints { public class Endpoint { - public string Name; - public string Uri; + public string Name = string.Empty; + public string Uri = string.Empty; } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoints.cs b/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoints.cs index d907d59..b2e85cf 100644 --- a/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoints.cs +++ b/Zune.Net.MetaServices/DomainModels/Endpoints/Endpoints.cs @@ -6,6 +6,6 @@ namespace Zune.Net.MetaServices.DomainModels.Endpoints public class Endpoints { [XmlElement("ENDPOINT")] - public List endpoints; + public List endpoints = new(); } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs index 9bf1f04..2365d99 100644 --- a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCd.cs @@ -5,68 +5,69 @@ namespace Zune.Net.MetaServices.DomainModels.MdarCd public class MdarCd { [XmlElement("A_id")] - public string AId {get; set;} + public string AId {get; set;} = string.Empty; [XmlElement("Title")] - public string Title { get; set; } + public string Title { get; set; } = string.Empty; [XmlElement("AlbumId")] - public long AlbumId { get; set; } + public long? AlbumId { get; set; } [XmlElement("Volume")] - public int Volume { get; set; } + public int? Volume { get; set; } [XmlElement("track")] - public List Items { get; set; } + public List Items { get; set; } = new(); [XmlElement("ReturnCode")] public string ReturnCode = "SUCCESS"; [XmlElement("WmCollectionId")] - public Guid AlbumMBID { get; set; } + public Guid? AlbumMBID { get; set; } [XmlElement("WmCollectiongroupId")] - public Guid AlbumGroupMBID { get; set; } + public Guid? AlbumGroupMBID { get; set; } [XmlElement("AlbumWmid")] - public Guid AlbumWmid { get; set; } + public Guid? AlbumWmid { get; set; } [XmlElement("ArtistWmid")] - public Guid ArtistWmid { get; set; } + public Guid? ArtistWmid { get; set; } [XmlElement("uniqueFileID")] - public string UniqueFileID { get; set; } + public string UniqueFileID { get; set; } = string.Empty; [XmlElement("Source")] public string Source { get; set; } = "AMG"; [XmlElement("SmallCoverArtURL")] - public string SmallCoverArtURL { get; set; } + public string SmallCoverArtURL { get; set; } = string.Empty; [XmlElement("LargeCoverArtURL")] - public string LargeCoverArtURL { get; set; } + public string LargeCoverArtURL { get; set; } = string.Empty; [XmlElement("ReleaseDate")] - public string DateTime { get; set; } + public string DateTime { get; set; } = string.Empty; + // The default value of " " was found in an example response via google. [XmlElement("Rating")] public string Rating { get; set; } = " "; [XmlElement("PerformerName")] - public string ArtistName { get; set; } + public string ArtistName { get; set; } = string.Empty; [XmlElement("MoreInfoLink")] - public string MoreInfoID { get; set; } + public string MoreInfoID { get; set; } = string.Empty; [XmlElement("Label")] - public string LabelName { get; set; } + public string LabelName { get; set; } = string.Empty; // Till the response correctly identifies _which_ files are matches, this must be false. Otherwise, you end up with doubled matches [XmlElement("IsExactMatch")] public bool IsExactMatch { get; set; } = false; [XmlElement("Genre")] - public string Genre { get; set; } + public string Genre { get; set; } = string.Empty; [XmlElement("DataProviderParams")] public string DataProviderParams { get; set; } = "Provider=AMG"; @@ -75,6 +76,6 @@ public class MdarCd public string DataProviderLogo { get; set; } = "Provider=AMG"; [XmlElement("BuyNowLink")] - public string BuyNowLink { get; set; } + public string BuyNowLink { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs index 44d1ce1..16b6411 100644 --- a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarCdRequestMetadata.cs @@ -6,10 +6,10 @@ namespace Zune.Net.MetaServices.DomainModels.MdarCd public class MdarCdRequestMetadata { [XmlElement("mdqRequestID")] - public Guid mdqRequestID{get; set;} + public Guid? mdqRequestID{get; set;} [XmlElement("MDAR-CD")] - public MdarCd MdarCd {get; set;} + public MdarCd MdarCd {get; set;} = new(); [XmlElement("BackOff")] public MdarBackoff Backoff = new(); diff --git a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs index fb9a092..42c4b3e 100644 --- a/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs +++ b/Zune.Net.MetaServices/DomainModels/MDAR-CD/MdarTrack.cs @@ -5,16 +5,16 @@ namespace Zune.Net.MetaServices.DomainModels.MdarCd public class MdarTrack { [XmlElement("Title")] - public string Title { get; set; } + public string Title { get; set; } = string.Empty; [XmlElement("Performers")] - public string Performers { get; set; } + public string Performers { get; set; } = string.Empty; [XmlElement("TrackNum")] - public int TrackNumber { get; set; } + public int TrackNumber { get; set; } = 0; [XmlElement("TrackWmid")] - public Guid TrackWmid {get; set;} + public Guid? TrackWmid {get; set;} [XmlElement("TrackRequestID")] public int TrackRequestID {get; set;} = 0; diff --git a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqAlbum.cs b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqAlbum.cs index 8e83b28..a5664f4 100644 --- a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqAlbum.cs +++ b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqAlbum.cs @@ -5,9 +5,9 @@ namespace Zune.Net.MetaServices.DomainModels.MdqCd public class MdqAlbum { [XmlElement("title")] - public MdqDescriptionElement AlbumTitle; + public MdqDescriptionElement AlbumTitle = new(); [XmlElement("artist")] - public MdqDescriptionElement AlbumArtist; + public MdqDescriptionElement AlbumArtist = new(); } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqCd.cs b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqCd.cs index 4e60189..ce8cd09 100644 --- a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqCd.cs +++ b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqCd.cs @@ -5,12 +5,12 @@ namespace Zune.Net.MetaServices.DomainModels.MdqCd public class MdqCd { [XmlElement("mdqRequestID")] - public string MdqRequestId { get; set; } + public string MdqRequestId { get; set; } = string.Empty; [XmlElement("album")] - public MdqAlbum Album { get; set; } + public MdqAlbum Album { get; set; } = new(); [XmlElement("track")] - public List Tracks { get; set; } + public List Tracks { get; set; } = new(); } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqDescriptionElement.cs b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqDescriptionElement.cs index 4abcec9..9ce64c1 100644 --- a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqDescriptionElement.cs +++ b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqDescriptionElement.cs @@ -5,9 +5,9 @@ namespace Zune.Net.MetaServices.DomainModels.MdqCd public class MdqDescriptionElement { [XmlElement("text")] - public string Text; + public string Text = string.Empty; [XmlArrayItem("word")] - public List Words; + public List Words = new(); } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqRequestMetadata.cs b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqRequestMetadata.cs index 7f5bd77..651f717 100644 --- a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqRequestMetadata.cs +++ b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqRequestMetadata.cs @@ -6,6 +6,6 @@ namespace Zune.Net.MetaServices.DomainModels.MdqCd public partial class MdqRequestMetadata { [XmlElement("MDQ-CD")] - public MdqCd MdqCd { get; set; } + public MdqCd MdqCd { get; set; } = new(); } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs index a630f85..8f296d9 100644 --- a/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs +++ b/Zune.Net.MetaServices/DomainModels/MDQ-CD/MdqTrack.cs @@ -5,28 +5,28 @@ namespace Zune.Net.MetaServices.DomainModels.MdqCd public class MdqTrack { [XmlElement("title")] - public MdqDescriptionElement Title; + public MdqDescriptionElement Title = new(); [XmlElement("artist")] - public MdqDescriptionElement Artist; + public MdqDescriptionElement Artist = new(); [XmlElement("trackNumber")] - public int TrackNumber; + public int TrackNumber = 0; [XmlElement("trackDuration")] - public int TrackDurationMs; + public int TrackDurationMs = 0; [XmlElement("filename")] - public string TrackFilename; + public string TrackFilename = string.Empty; [XmlElement("bitrate")] - public int Bitrate; + public int Bitrate = 0; [XmlElement("drmProtected")] public int DRMProtected = 0; [XmlElement("trackRequestID")] - public int trackRequestId; + public int trackRequestId = 0; [XmlArray("trace")] public List Trace = new(); } diff --git a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs index 5ff575c..19d4a27 100644 --- a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs +++ b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrCd.cs @@ -9,14 +9,14 @@ public class MDRCD public Guid MdqRequestID { get; set; } = Guid.Empty; [XmlElement("uniqueFileID")] - public string UniqueFileID { get; set; } + public string UniqueFileID { get; set; } = string.Empty; [XmlElement("publisherRating")] - public string PublisherRating { get; set; } + public string PublisherRating { get; set; } = string.Empty; // i.e. providerName=AMG&albumID=DC1A65D9-C8A4-4190-87CB-F871B8AC6D37&a_id=R%20%203021180&album=On%20Empty&artistID=1A9ECD8B-230E-49CD-B97E-1C631530581C&p_id=P%20%202986084&artist=Kevin%20Calder [XmlElement("buyParams")] - public string BuyParams { get; set; } + public string BuyParams { get; set; } = string.Empty; [XmlElement("dataProvider")] public string DataProvider { get; set; } = "AMG"; @@ -37,41 +37,41 @@ public class MDRCD public Guid WMCollectionGroupID { get; set; } [XmlElement("albumTitle")] - public string AlbumTitle { get; set; } + public string AlbumTitle { get; set; } = string.Empty; [XmlElement("albumArtist")] - public string AlbumArtist { get; set; } + public string AlbumArtist { get; set; } = string.Empty; [XmlElement("releaseDate")] public DateTime ReleaseDate { get; set; } [XmlElement("label")] - public string Label { get; set; } + public string Label { get; set; } = string.Empty; [XmlElement("genre")] - public string Genre { get; set; } + public string Genre { get; set; } = string.Empty; // i.e. "Pop/Rock" [XmlElement("providerStyle")] - public string AlbumStyle { get; set; } + public string AlbumStyle { get; set; } = string.Empty; [XmlElement("needsIDs")] public int NeedIDs { get; set; } = 0; // i.e. 200/drW500/W564/W56460UCJS0.jpg [XmlElement("largeCoverParams")] - public string LargeCoverAddress { get; set; } + public string LargeCoverAddress { get; set; } = string.Empty; // i.e. 075/drW500/W564/W56460UCJS0.jpg [XmlElement("smallCoverParams")] - public string SmallCoverAddress { get; set; } + public string SmallCoverAddress { get; set; } = string.Empty; // i.e. a_id=R%20%203021180 [XmlElement("moreInfoParams")] - public string MoreInfoId { get; set; } + public string MoreInfoId { get; set; } = string.Empty; [XmlElement("track")] - public List Track { get; set; } + public List Track { get; set; } = new(); [XmlElement("Volume")] public int VolumeNumber { get; set; } diff --git a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs index 0d36fdb..0ac833b 100644 --- a/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs +++ b/Zune.Net.MetaServices/DomainModels/MDR-CD/MdrRequestMetadata.cs @@ -7,14 +7,14 @@ public class MdrRequestMetadata { [XmlElement("AlbumId")] - public Guid AlbumId { get; set; } + public Guid? AlbumId { get; set; } [XmlElement("MDR-CD")] - public MDRCD MDRCD { get; set; } + public MDRCD MDRCD { get; set; } = new(); [XmlElement("Backoff")] - public Backoff Backoff { get; set; } + public Backoff Backoff { get; set; } = new(); [XmlElement("mdqRequestID")] - public Guid MdqRequestID { get; set; } + public Guid? MdqRequestID { get; set; } } } diff --git a/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs index 6f2820f..6ab1674 100644 --- a/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs +++ b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbum.cs @@ -5,16 +5,16 @@ namespace Zune.Net.MetaServices.DomainModels.MdsrCd public class MdsrAlbum { [XmlElement("albumFullTitle")] - public string Title { get; set; } + public string Title { get; set; } = string.Empty; [XmlElement("id_album")] public long AlbumId { get; set; } [XmlElement("albumPerformer")] - public string AlbumArtist { get; set; } + public string AlbumArtist { get; set; } = string.Empty; [XmlElement("albumGenre")] - public string Genre { get; set; } + public string Genre { get; set; } = string.Empty; [XmlElement("Volume")] public int Volume { get; set; } @@ -32,9 +32,9 @@ public class MdsrAlbum public bool IsMultiDisc { get; set; } [XmlElement("albumCover")] - public string CoverParms { get; set; } + public string CoverParms { get; set; } = string.Empty; [XmlElement("buyNowLink")] - public string BuyNowParms { get; set; } + public string BuyNowParms { get; set; } = string.Empty; } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumRequestMetadata.cs b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumRequestMetadata.cs index db46eb1..5c9383a 100644 --- a/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumRequestMetadata.cs +++ b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumRequestMetadata.cs @@ -9,6 +9,6 @@ namespace Zune.Net.MetaServices.DomainModels.MdsrCd public class MdsrAlbumRequestMetadata { [XmlElement("MDSR-CD")] // of type (UIX) Album - public MdsrAlbumSearchResult mDSRcD {get; set;} + public MdsrAlbumSearchResult mDSRcD {get; set;} = new(); } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumSearchResult.cs b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumSearchResult.cs index 6ca0f0c..a30d2af 100644 --- a/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumSearchResult.cs +++ b/Zune.Net.MetaServices/DomainModels/MDSR-CD/MdsrAlbumSearchResult.cs @@ -8,6 +8,6 @@ public class MdsrAlbumSearchResult { [XmlArray("SearchResult")] [XmlArrayItem("Result")] - public List Results; + public List Results = new(); } } \ No newline at end of file diff --git a/Zune.Net.MetaServices/Models/WMIS.cs b/Zune.Net.MetaServices/Models/WMIS.cs index 1bcb66a..2ef873a 100644 --- a/Zune.Net.MetaServices/Models/WMIS.cs +++ b/Zune.Net.MetaServices/Models/WMIS.cs @@ -135,7 +135,7 @@ await Parallel.ForEachAsync(release.Media[0].Tracks, async (track, ct) => }; } - private async Task> GetTracksFromIReleaseAsync(IRelease release, MdqRequestMetadata requestMetadata = null) + private async Task> GetTracksFromIReleaseAsync(IRelease release, MdqRequestMetadata? requestMetadata) { var tracks = new List(); if (release.Media != null && release.Media.Count > 0) @@ -340,7 +340,7 @@ public async Task GetMdrCdRequestFromTrackIdAsync(int TrackR }}, Label = label, UniqueFileID = $"AMGa_ID=R {albumId}", - AlbumTitle = release.Title, + AlbumTitle = release.Title!, AlbumArtist = performerName, ReleaseDate = release.Date.NearestDate, Genre = genre, diff --git a/Zune.Net.MetaServices/appsettings.json b/Zune.Net.MetaServices/appsettings.json index 23cbe92..3efd2fe 100644 --- a/Zune.Net.MetaServices/appsettings.json +++ b/Zune.Net.MetaServices/appsettings.json @@ -6,7 +6,6 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" diff --git a/Zune.Net.Mix/Controllers/AlbumController.cs b/Zune.Net.Mix/Controllers/AlbumController.cs new file mode 100644 index 0000000..6026025 --- /dev/null +++ b/Zune.Net.Mix/Controllers/AlbumController.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Mvc; +using Zune.Net.Helpers; +using Zune.Net.Mix.DomainModel; + +namespace Zune.Net.Mix.Controllers +{ + [Route("/v4.0/{culture}/model/album/")] + [Produces("application/xml")] + public class AlbumController : Controller + { + private ILogger _logger; + public AlbumController(ILogger logger) + { + _logger = logger; + } + + [HttpGet("{mbid}")] + public async Task> SimilarAlbum(Guid mbid) + { + // First, get all of the tracks on an album MBID + var trackList = await MusicBrainz.GetTracksByAlbumMbidAsync(mbid); + + // now, get all the genreIDs + var genreIds = await MusicBrainz.GetGenreIdsByAlbumMbidAsync(mbid); + + // If we don't have enough data, 404. The API will eventually rerequest this. + if(genreIds.Count == 0 || trackList.Count == 0) + { + _logger.LogInformation($"No genre data available for albumId{mbid}"); + return NotFound(); + } + + var response = new AlbumModel(); + + foreach(var trackId in trackList) + { + response.Entry.Add(new(trackId, genreIds)); + } + + _logger.LogInformation($"Returning {genreIds.Count} genres and {trackList.Count} tracks for albumId: {mbid}"); + + return Ok(response); + } + } +} diff --git a/Zune.Net.Mix/Controllers/ArtistController.cs b/Zune.Net.Mix/Controllers/ArtistController.cs index 4682162..c3a9883 100644 --- a/Zune.Net.Mix/Controllers/ArtistController.cs +++ b/Zune.Net.Mix/Controllers/ArtistController.cs @@ -1,3 +1,4 @@ +using System.Threading.Tasks; using Atom.Xml; using Microsoft.AspNetCore.Mvc; using Zune.Net.Helpers; @@ -5,15 +6,35 @@ namespace Zune.Net.Mix.Controllers { - [Route("/v4.0/{culture}/artist/")] - [Produces(Atom.Constants.ATOM_MIMETYPE)] + [Route("/v4.0/{culture}/")] public class ArtistController : Controller { + private ILogger _logger; + public ArtistController(ILogger logger) + { + _logger = logger; + } - [HttpGet, Route("{mbid}")] + [HttpGet("artist/{mbid}")] + [Produces(Atom.Constants.ATOM_MIMETYPE)] public async Task>> SimilarArtists(Guid mbid) { return await LastFM.GetSimilarArtistsByMBID(mbid); } + + [HttpGet("model/artist/{mbid}")] + [Produces("application/xml")] + public async Task> SimilarArtistsModel(Guid mbid) + { + var artistGenreIds = await MusicBrainz.GetArtistGenreIdsByArtistIdAsync(mbid); + if(artistGenreIds.Count == 0) + { + _logger.LogInformation($"No genres were found for artistId: {mbid}"); + return NotFound(); + } + + _logger.LogInformation($"Returning {artistGenreIds.Count} genres for artistId: {mbid}"); + return Ok(new VectorEntry(mbid, artistGenreIds)); + } } } diff --git a/Zune.Net.Mix/Controllers/TrackController.cs b/Zune.Net.Mix/Controllers/TrackController.cs index 33aace0..1caf603 100644 --- a/Zune.Net.Mix/Controllers/TrackController.cs +++ b/Zune.Net.Mix/Controllers/TrackController.cs @@ -1,4 +1,6 @@ -using Atom.Xml; +using System; +using System.Threading.Tasks; +using Atom.Xml; using Microsoft.AspNetCore.Mvc; using Zune.Net.Helpers; using Zune.Xml.Catalog; @@ -10,7 +12,7 @@ namespace Zune.Net.Mix.Controllers public class TrackController : Controller { - [HttpGet, Route("{mbid}/similarTracks")] + [HttpGet("{mbid}/similarTracks")] public async Task>> SimilarTracks(Guid mbid) { return await LastFM.GetSimilarTracksByMBID(mbid); diff --git a/Zune.Net.Mix/DomainModel/AlbumModel.cs b/Zune.Net.Mix/DomainModel/AlbumModel.cs new file mode 100644 index 0000000..288254b --- /dev/null +++ b/Zune.Net.Mix/DomainModel/AlbumModel.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; +namespace Zune.Net.Mix.DomainModel +{ + [XmlRoot(ElementName = "feed")] + public class AlbumModel + { + [XmlElement(ElementName = "updated")] + public DateTime Updated { get; set; } = DateTime.Now; + + [XmlElement(ElementName = "entry")] + public List Entry { get; set; } = new(); + } +} diff --git a/Zune.Net.Mix/DomainModel/VectorEntry.cs b/Zune.Net.Mix/DomainModel/VectorEntry.cs new file mode 100644 index 0000000..1bac34b --- /dev/null +++ b/Zune.Net.Mix/DomainModel/VectorEntry.cs @@ -0,0 +1,35 @@ +using System.Xml.Serialization; + +namespace Zune.Net.Mix +{ + [XmlRoot(ElementName = "entry")] + public class VectorEntry + { + public VectorEntry() { } + public VectorEntry(Guid mbid, IEnumerable genreIds) + { + var genreList = string.Join(",", genreIds); + Vector = $"0,{genreList}"; + ItemMbid = mbid; + } + + [XmlElement(ElementName = "updated")] + public DateTime Updated { get; set; } = DateTime.Now; + + [XmlElement(ElementName = "id")] + public Guid ItemMbid { get; set; } = Guid.Empty; + + [XmlElement(ElementName = "context")] + public int Context { get; set; } = 12; + + [XmlElement(ElementName = "schemeId")] + public int SchemeId { get; set; } = 1; + + // the vector is... interesting. its a combo of generes by int, prepended with "0," for reasons.. Use the constructor to make one. + [XmlElement(ElementName = "vector")] + public string? Vector { get; set; } + + [XmlElement(ElementName = "expirationDate")] + public DateTime ExpirationDate { get; set; } = DateTime.Now + TimeSpan.FromDays(14); + } +} diff --git a/Zune.Net.Mix/appsettings.json b/Zune.Net.Mix/appsettings.json index 10f68b8..cdcf7bb 100644 --- a/Zune.Net.Mix/appsettings.json +++ b/Zune.Net.Mix/appsettings.json @@ -2,7 +2,6 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" diff --git a/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs b/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs index 56f0db4..bb5c659 100644 --- a/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs +++ b/Zune.Net.Shared/Helpers/MusicBrainz.Album.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Zune.Xml.Catalog; namespace Zune.Net.Helpers @@ -71,7 +72,7 @@ public static Album MBReleaseToAlbum(IRelease mb_rel, DateTime? updated = null, { album.Tracks = new(); - for(var mediaId = 0; mediaId < mb_rel.Media.Count; mediaId++) + for (var mediaId = 0; mediaId < mb_rel.Media.Count; mediaId++) { var mb_media = mb_rel.Media[mediaId]; @@ -101,5 +102,62 @@ public static MiniAlbum MBReleaseToMiniAlbum(IRelease mb_rel) Title = mb_rel.Title }; } + + public static async Task> GetTracksByAlbumMbidAsync(Guid albumId) + { + var trackList = new List(); + try + { + var album = await _query.LookupReleaseAsync(albumId, Include.Recordings); + var medias = album.Media?.ToList() ?? new List(); + medias.ForEach(media => media.Tracks.ToList().ForEach(track => trackList.Add(track.Id))); + } + catch { } + return trackList; + } + + public static async Task> GetGenreIdsByReleaseGroupMbidAsync(Guid releaseGroupId) + { + try + { + var genreIdList = new List(); + var releaseGroup = await _query.LookupReleaseGroupAsync(releaseGroupId, Include.Tags | Include.Genres); + foreach (var genre in releaseGroup.Genres?.ToList() ?? new List()) + { + var genreIndexId = Array.IndexOf(MusicBrainzGenreList.Genres, genre.Name); + if (genreIndexId > -1) + { + genreIdList.Add(genreIndexId); + } + } + + foreach (var tag in releaseGroup.Tags?.ToList() ?? new List()) + { + var genreIndexId = Array.IndexOf(MusicBrainzGenreList.Genres, tag.Name); + if (genreIndexId > -1) + { + genreIdList.Add(genreIndexId); + } + } + + return genreIdList; + } catch + { + return new List(); + } + } + + public static async Task> GetGenreIdsByAlbumMbidAsync(Guid albumId) + { + try + { + var album = await _query.LookupReleaseAsync(albumId, Include.ReleaseGroups); + return await GetGenreIdsByReleaseGroupMbidAsync(album.ReleaseGroup.Id); + } + catch + { + return new List(); + } + } } } diff --git a/Zune.Net.Shared/Helpers/MusicBrainz.Artist.cs b/Zune.Net.Shared/Helpers/MusicBrainz.Artist.cs index fa9a68d..bb53096 100644 --- a/Zune.Net.Shared/Helpers/MusicBrainz.Artist.cs +++ b/Zune.Net.Shared/Helpers/MusicBrainz.Artist.cs @@ -2,7 +2,9 @@ using MetaBrainz.MusicBrainz; using MetaBrainz.MusicBrainz.Interfaces.Entities; using System; +using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Zune.Xml.Catalog; namespace Zune.Net.Helpers @@ -146,5 +148,21 @@ public static MiniArtist MBArtistToMiniArtist(IArtist mb_artist) Title = mb_artist.Name }; } + + public static async Task> GetArtistGenreIdsByArtistIdAsync(Guid artistId) + { + var artistGenreIds = new HashSet(); + + var artistDetails = _query.LookupArtist(artistId, Include.ReleaseGroups); + var releaseGroupIds = artistDetails.ReleaseGroups?.Select(x=> x.Id) ?? new List(); + + foreach(var releaseGroup in releaseGroupIds) + { + var results = await MusicBrainz.GetGenreIdsByReleaseGroupMbidAsync(releaseGroup); + results.ForEach(genreId => artistGenreIds.Add(genreId)); + } + + return artistGenreIds.ToList(); + } } } diff --git a/Zune.Net.Shared/Helpers/MusicBrainzGenreList.cs b/Zune.Net.Shared/Helpers/MusicBrainzGenreList.cs new file mode 100644 index 0000000..190c8b3 --- /dev/null +++ b/Zune.Net.Shared/Helpers/MusicBrainzGenreList.cs @@ -0,0 +1,1794 @@ +namespace Zune.Net.Helpers +{ + public static class MusicBrainzGenreList + { + // fetched with https://musicbrainz.org/ws/2/genre/all?fmt=txt, saved off for index stability + public static string[] Genres = + { + "2 tone", + "2-step", + "aak", + "abhang", + "aboio", + "abstract hip hop", + "acid breaks", + "acid house", + "acid jazz", + "acid rock", + "acid techno", + "acid trance", + "acidcore", + "acousmatic", + "acoustic blues", + "acoustic chicago blues", + "acoustic rock", + "acoustic texas blues", + "adhunik geet", + "afoxê", + "african blues", + "afro house", + "afro rock", + "afro trap", + "afro-cuban jazz", + "afro-funk", + "afro-jazz", + "afro-zouk", + "afrobeat", + "afrobeats", + "afropiano", + "afroswing", + "agbadza", + "agbekor", + "aggrotech", + "ahwash", + "aita", + "akishibu-kei", + "al jeel", + "algerian chaabi", + "algorave", + "alloukou", + "alpenrock", + "alternative country", + "alternative dance", + "alternative folk", + "alternative hip hop", + "alternative metal", + "alternative pop", + "alternative punk", + "alternative r&b", + "alternative rock", + "amapiano", + "ambasse bey", + "ambient", + "ambient americana", + "ambient dub", + "ambient house", + "ambient noise wall", + "ambient pop", + "ambient techno", + "ambient trance", + "ambrosian chant", + "american primitive guitar", + "americana", + "anarcho-punk", + "anatolian rock", + "andalusian classical", + "andean new age", + "anglican chant", + "animal sounds", + "anti-folk", + "aor", + "apala", + "appalachian folk", + "arabesk", + "arabesk rap", + "arena rock", + "arrocha", + "arrocha funk", + "arrocha sertanejo", + "arrochadeira", + "ars antiqua", + "ars nova", + "ars subtilior", + "art pop", + "art punk", + "art rock", + "art song", + "artcore", + "asmr", + "assiko", + "atmospheric black metal", + "atmospheric drum and bass", + "atmospheric sludge metal", + "audio drama", + "avant-folk", + "avant-garde", + "avant-garde jazz", + "avant-garde metal", + "avant-garde pop", + "avant-prog", + "avtorskaya pesnya", + "axé", + "bachata", + "bachatón", + "bagad", + "baião", + "baila", + "baisha xiyue", + "baithak gana", + "bakersfield sound", + "balani show", + "balearic beat", + "balearic trance", + "balinese gamelan", + "balitaw", + "ballad", + "ballad opera", + "ballet", + "ballroom house", + "baltimore club", + "bambuco", + "banda sinaloense", + "bandari", + "bandinha", + "barber beats", + "barbershop", + "bard rock", + "bardcore", + "baroque", + "baroque pop", + "baroque suite", + "bass house", + "bassline", + "batida", + "batidão romântico", + "battle rap", + "batucada", + "batuque", + "baul gaan", + "beat bruxaria", + "beat music", + "beat poetry", + "beatboxing", + "beatdown hardcore", + "bebop", + "bedroom pop", + "beijing opera", + "bélé", + "belgian techno", + "bend-skin", + "beneventan chant", + "benga", + "benna", + "bérite club", + "berlin school", + "bhajan", + "bhangra", + "bhavageethe", + "big band", + "big beat", + "big room house", + "big room trance", + "biguine", + "bikutsi", + "binaural beats", + "biraha", + "birdsong", + "birmingham sound", + "bit music", + "bitpop", + "black 'n' roll", + "black ambient", + "black metal", + "black midi", + "black noise", + "blackened crust", + "blackened death metal", + "blackgaze", + "bleep techno", + "blue-eyed soul", + "bluegrass", + "bluegrass gospel", + "blues", + "blues rock", + "bocet", + "boduberu", + "boedra", + "bogino duu", + "bolero", + "bolero español", + "bolero son", + "bolero-beat", + "bomba", + "bomba del chota", + "bongo flava", + "boogaloo", + "boogie", + "boogie rock", + "boogie-woogie", + "boom bap", + "bossa nova", + "bounce", + "bouncy techno", + "bouyon", + "brass band", + "brazilian bass", + "break-in", + "breakbeat", + "breakbeat hardcore", + "breakbeat kota", + "breakcore", + "breaks", + "breakstep", + "brega", + "brega calypso", + "brega funk", + "brill building", + "brit funk", + "britcore", + "british blues", + "british brass band", + "british folk rock", + "british rhythm & blues", + "britpop", + "bro-country", + "broken beat", + "broken transmission", + "brostep", + "brutal death metal", + "brutal prog", + "bubblegum bass", + "bubblegum dance", + "bubblegum pop", + "bubbling", + "bubbling house", + "budots", + "bulería", + "bullerengue", + "burger-highlife", + "burmese classical", + "burmese mono", + "burmese stereo", + "burning spirits", + "bytebeat", + "byzantine chant", + "c-pop", + "c86", + "ca trù", + "cabaret", + "cabo zouk", + "cadence lypso", + "cadence rampa", + "cải lương", + "cajun", + "cakewalk", + "čalgija", + "calipso venezolano", + "calypso", + "campursari", + "campus folk", + "canción melódica", + "candombe", + "candombe beat", + "cantata", + "cante alentejano", + "canterbury scene", + "canto a lo poeta", + "canto cardenche", + "cantonese opera", + "cantopop", + "cantoria", + "cantu a chiterra", + "cantu a tenore", + "canzone d'autore", + "canzone napoletana", + "canzone neomelodica", + "cape breton fiddling", + "cape jazz", + "carimbó", + "carnatic classical", + "carnavalito", + "carranga", + "celtic", + "celtic electronica", + "celtic metal", + "celtic new age", + "celtic punk", + "celtic rock", + "central asian throat singing", + "chacarera", + "chachachá", + "chalga", + "chamamé", + "chamarrita açoriana", + "chamarrita rioplatense", + "chamber folk", + "chamber pop", + "champeta", + "changa tuki", + "changüí", + "chanson à texte", + "chanson française", + "chanson réaliste", + "charanga", + "chazzanut", + "chèo", + "chicago blues", + "chicago bop", + "chicago drill", + "chicago house", + "chicago soul", + "chicano rap", + "chilena", + "chillout", + "chillstep", + "chillsynth", + "chillwave", + "chimurenga", + "chinese classical", + "chinese opera", + "chinese revolutionary opera", + "chipmunk soul", + "chiptune", + "chopped and screwed", + "choral symphony", + "choro", + "chotis madrileño", + "christian hip hop", + "christian metal", + "christian rock", + "christmas music", + "church music", + "chutney", + "chutney soca", + "cilokaq", + "cinematic classical", + "ciranda", + "circus march", + "city pop", + "classic blues", + "classic country", + "classic jazz", + "classic rock", + "classical", + "classical crossover", + "classical period", + "close harmony", + "cloud rap", + "club", + "cocktail nation", + "coco", + "coladeira", + "coldwave", + "colindă", + "colour bass", + "comedy", + "comedy hip hop", + "comedy rock", + "comfy synth", + "compas", + "complextro", + "concerto", + "concerto for orchestra", + "concerto grosso", + "conducted improvisation", + "conga", + "congolese rumba", + "conscious hip hop", + "contemporary christian", + "contemporary classical", + "contemporary country", + "contemporary folk", + "contemporary gospel", + "contemporary jazz", + "contemporary r&b", + "contra", + "cool jazz", + "coon song", + "copla", + "corrido", + "corrido tumbado", + "country", + "country and irish", + "country blues", + "country boogie", + "country folk", + "country gospel", + "country pop", + "country rap", + "country rock", + "country soul", + "country yodeling", + "countrypolitan", + "coupé-décalé", + "cowpunk", + "crack rock steady", + "crime jazz", + "crossbreed", + "crossover jazz", + "crossover prog", + "crossover thrash", + "cruise", + "crunk", + "crunkcore", + "crust punk", + "csárdás", + "cuarteto", + "cubatón", + "cueca", + "cumbia", + "cumbia argentina", + "cumbia colombiana", + "cumbia mexicana", + "cumbia norteña mexicana", + "cumbia peruana", + "cumbia pop", + "cumbia santafesina", + "cumbia sonidera", + "cumbia turra", + "cumbia villera", + "cumbiatón", + "cuplé", + "currulao", + "cyber metal", + "cybergrind", + "cyberpunk", + "d-beat", + "dabke", + "dance", + "dance-pop", + "dance-punk", + "dance-rock", + "dancefloor drum and bass", + "dancehall", + "dangak", + "dangdut", + "danmono", + "dansband", + "dansktop", + "danzón", + "dark ambient", + "dark cabaret", + "dark disco", + "dark electro", + "dark folk", + "dark jazz", + "dark plugg", + "dark psytrance", + "dark wave", + "darkcore", + "darkstep", + "darksynth", + "data sonification", + "death 'n' roll", + "death industrial", + "death metal", + "death-doom metal", + "deathcore", + "deathgrind", + "deathrock", + "deathstep", + "deconstructed club", + "deep funk", + "deep house", + "deep soul", + "deep tech", + "deep techno", + "delta blues", + "dembow", + "dennery segment", + "denpa", + "depressive black metal", + "descarga", + "desert blues", + "desert rock", + "detroit techno", + "detroit trap", + "dhaanto", + "dhrupad", + "digicore", + "digital cumbia", + "digital fusion", + "digital hardcore", + "dimotiko", + "dirty south", + "disco", + "disco polo", + "dissonant death metal", + "diva house", + "divertissement", + "dixieland", + "djanba", + "djent", + "dobrado", + "doina", + "dongjing", + "donk", + "donosti sound", + "doo-wop", + "doom metal", + "doomcore", + "doskpop", + "downtempo", + "dream pop", + "dream trance", + "dreampunk", + "drift phonk", + "drill", + "drill and bass", + "drone", + "drone metal", + "drum and bass", + "drumfunk", + "drumless hip hop", + "drumline", + "drumstep", + "dub", + "dub poetry", + "dub techno", + "dubstep", + "dubstyle", + "dubwise", + "duma", + "dunedin sound", + "dungeon sound", + "dungeon synth", + "duranguense", + "dutch house", + "eai", + "east coast hip hop", + "easy listening", + "easycore", + "ebm", + "edm", + "electric blues", + "electric texas blues", + "electro", + "electro house", + "electro latino", + "electro swing", + "electro-disco", + "electro-funk", + "electro-industrial", + "electroacoustic", + "electroclash", + "electronic", + "electronic rock", + "electronica", + "electronicore", + "electropop", + "electropunk", + "electrotango", + "eleki", + "embolada", + "emo", + "emo pop", + "emo rap", + "emocore", + "emoviolence", + "enka", + "éntekhno", + "epic collage", + "epic doom metal", + "estrada", + "ethereal wave", + "ethio-jazz", + "étude", + "euphoric hardstyle", + "euro house", + "euro-disco", + "euro-trance", + "eurobeat", + "eurodance", + "europop", + "euskal kantagintza berria", + "exotica", + "experimental", + "experimental big band", + "experimental electronic", + "experimental hip hop", + "experimental rock", + "expressionism", + "extratone", + "fado", + "fado de coimbra", + "fairy tale", + "fakaseasea", + "falak", + "famo", + "fandango", + "fandango caiçara", + "fantasia", + "fantezi", + "festejo", + "festival progressive house", + "festival trap", + "fidget house", + "field recording", + "fife and drum", + "fife and drum blues", + "filin", + "filk", + "filmi", + "finnish tango", + "flamenco", + "flamenco jazz", + "flamenco pop", + "flashcore", + "flex dance music", + "florida breaks", + "fm synthesis", + "folk", + "folk metal", + "folk pop", + "folk punk", + "folk rock", + "folkhop", + "folktronica", + "fon leb", + "footwork", + "footwork jungle", + "forest psytrance", + "forró", + "forró eletrônico", + "forró universitário", + "freak folk", + "freakbeat", + "free folk", + "free improvisation", + "free jazz", + "free tekno", + "freeform hardcore", + "freestyle", + "french electro", + "french house", + "frenchcore", + "frevo", + "fuji", + "full-on", + "funaná", + "funeral doom metal", + "funeral march", + "fungi", + "funk", + "funk carioca", + "funk mandelão", + "funk melody", + "funk metal", + "funk ostentação", + "funk proibidão", + "funk rock", + "funknejo", + "funkot", + "funktronica", + "funky house", + "future bass", + "future bounce", + "future core", + "future funk", + "future garage", + "future house", + "future rave", + "future riddim", + "futurepop", + "futurism", + "g-funk", + "g-house", + "gabber", + "gaelic psalm singing", + "gagaku", + "gagok", + "gaita zuliana", + "gambang kromong", + "gamelan", + "gamelan angklung", + "gamelan beleganjur", + "gamelan degung", + "gamelan gender wayang", + "gamelan gong gede", + "gamelan gong kebyar", + "gamelan jegog", + "gamelan joged bumbung", + "gamelan salendro", + "gamelan sekaten", + "gamelan selunding", + "gamelan semar pegulingan", + "gamelan siteran", + "gangsta rap", + "garage house", + "garage punk", + "garage rock", + "garage rock revival", + "garba", + "geek rock", + "genge", + "għana", + "ghazal", + "ghetto house", + "ghettotech", + "ginan", + "glam", + "glam metal", + "glam punk", + "glam rock", + "glitch", + "glitch hop", + "glitch hop edm", + "glitch pop", + "gnawa", + "go-go", + "goa trance", + "gondang", + "goombay", + "goregrind", + "gorenoise", + "gospel", + "gospel house", + "gospel reggae", + "gothic", + "gothic country", + "gothic metal", + "gothic rock", + "gqom", + "grand opera", + "grebo", + "gregorian chant", + "grime", + "grindcore", + "griot", + "groove metal", + "group sounds", + "grunge", + "guaguancó", + "guajira", + "guaracha", + "guaracha edm", + "guarania", + "guided meditation", + "guitarrada", + "gumbe", + "guoyue", + "gwo ka", + "gypsy jazz", + "gypsy punk", + "habanera", + "haitian vodou drumming", + "halftime", + "hambo", + "hamburger schule", + "hands up", + "happy hardcore", + "harawi", + "hard bop", + "hard drum", + "hard house", + "hard nrg", + "hard rock", + "hard techno", + "hard trance", + "hard trap", + "hardbag", + "hardbass", + "hardcore breaks", + "hardcore hip hop", + "hardcore punk", + "hardcore techno", + "hardgroove techno", + "hardstep", + "hardstyle", + "hardvapour", + "hardwave", + "harsh noise", + "harsh noise wall", + "hát tuồng", + "hauntology", + "heartland rock", + "heaven trap", + "heavy metal", + "heavy psych", + "heikyoku", + "hexd", + "hi-nrg", + "hi-tech", + "highlife", + "hill country blues", + "himene tarava", + "hindustani classical", + "hip hop", + "hip hop soul", + "hip house", + "hiplife", + "holy minimalism", + "honky tonk", + "honkyoku", + "hopepunk", + "horror punk", + "horror synth", + "horrorcore", + "house", + "huapango", + "huayno", + "humppa", + "hyangak", + "hybrid trap", + "hyper techno", + "hyperpop", + "hyphy", + "hypnagogic pop", + "idm", + "idol kayō", + "illbient", + "impressionism", + "indeterminacy", + "indian classical", + "indian pop", + "indie folk", + "indie pop", + "indie rock", + "indie surf", + "indietronica", + "indo jazz", + "indorock", + "industrial", + "industrial hardcore", + "industrial hip hop", + "industrial metal", + "industrial musical", + "industrial rock", + "industrial techno", + "instrumental", + "instrumental hip hop", + "instrumental jazz", + "instrumental rock", + "integral serialism", + "interview", + "iraqi maqam", + "irish folk", + "isicathamiya", + "islamic modal music", + "italo dance", + "italo house", + "italo-disco", + "izlan", + "izvorna bosanska muzika", + "j-core", + "j-pop", + "j-rock", + "jácara", + "jaipongan", + "jam band", + "jamaican ska", + "james bay fiddling", + "jamgrass", + "jangle pop", + "japanese classical", + "javanese gamelan", + "jazz", + "jazz blues", + "jazz fusion", + "jazz poetry", + "jazz pop", + "jazz rap", + "jazz rock", + "jazz-funk", + "jazzstep", + "jeongak", + "jerk rap", + "jersey club", + "jersey drill", + "jersey sound", + "jesus music", + "jiangnan sizhu", + "jit", + "jiuta", + "joik", + "jongo", + "joropo", + "jōruri", + "jota", + "jovem guarda", + "jug band", + "jùjú", + "juke", + "jump blues", + "jump up", + "jumpstyle", + "jungle", + "jungle dutch", + "jungle terror", + "junkanoo", + "k-pop", + "kabarett", + "kacapi suling", + "kadongo kamu", + "kafi", + "kagura", + "kalindula", + "kalon'ny fahiny", + "kaneka", + "kankyō ongaku", + "kantan chamorrita", + "kanto", + "kapuka", + "kaseko", + "kasékò", + "kawaii future bass", + "kawaii metal", + "kayōkyoku", + "kecak", + "keroncong", + "ketuk tilu", + "khrueang sai", + "khyal", + "kidandali", + "kidumbak", + "kilapanga", + "kirtan", + "kizomba", + "klapa", + "kleinkunst", + "klezmer", + "kliningan", + "könsrock", + "koplo", + "korean ballad", + "korean classical", + "korean revolutionary opera", + "kouta", + "krakowiak", + "krautrock", + "kuda lumping", + "kuduro", + "kujawiak", + "kulintang", + "kumi-daiko", + "kumiuta", + "kundiman", + "kwaito", + "kwassa kwassa", + "kwela", + "kyivan chant", + "laiko", + "lambada", + "landó", + "langgam jawa", + "latin", + "latin ballad", + "latin disco", + "latin funk", + "latin house", + "latin jazz", + "latin pop", + "latin rock", + "latin soul", + "lavani", + "lecture", + "leftfield", + "lento violento", + "levenslied", + "lied", + "liedermacher", + "liquid funk", + "liscio", + "livetronica", + "lo-fi", + "lo-fi hip hop", + "lo-fi house", + "lolicore", + "louisiana blues", + "lounge", + "lovers rock", + "lowercase", + "luk krung", + "luk thung", + "lundu", + "lute song", + "mad", + "madchester", + "maddahi", + "madrigal", + "maftirim", + "mahori", + "mahraganat", + "mainstream rock", + "makina", + "makossa", + "malagueña venezolana", + "malay gamelan", + "malhun", + "mallsoft", + "maloya", + "maloya élektrik", + "mambo", + "mambo chileno", + "mambo urbano", + "mandopop", + "manele", + "mangue beat", + "manila sound", + "manyao", + "marabi", + "maracatu", + "march", + "marching band", + "marchinha", + "mariachi", + "marinera", + "marrabenta", + "martial industrial", + "mashcore", + "maskanda", + "mass", + "math pop", + "math rock", + "mathcore", + "maxixe", + "mazurka", + "mbalax", + "mbaqanga", + "mbube", + "mchiriku", + "medieval", + "medieval lyric poetry", + "medieval metal", + "medieval rock", + "meiji shinkyoku", + "melbourne bounce", + "melodic black metal", + "melodic death metal", + "melodic dubstep", + "melodic hardcore", + "melodic house", + "melodic metalcore", + "melodic techno", + "melodic trance", + "mélodie", + "memphis rap", + "mento", + "menzuma", + "merecumbé", + "merengue", + "merengue típico", + "merenhouse", + "merequetengue", + "méringue", + "merseybeat", + "metal", + "metalcore", + "meyxana", + "miami bass", + "microhouse", + "microsound", + "microtonal classical", + "midtempo bass", + "midwest emo", + "miejski folk", + "milonga", + "min'yō", + "minatory", + "mincecore", + "minimal drum and bass", + "minimal synth", + "minimal techno", + "minimal wave", + "minimalism", + "minneapolis sound", + "minstrelsy", + "mobb music", + "mod", + "mod revival", + "moda de viola", + "modal jazz", + "modern blues", + "modern classical", + "modern creative", + "modern hardtek", + "modern laiko", + "modinha", + "monodrama", + "mood kayō", + "moogsploitation", + "moombahcore", + "moombahton", + "mor lam", + "mor lam sing", + "morna", + "moroccan chaabi", + "motet", + "motown", + "moutya", + "movimiento alterado", + "mozarabic chant", + "mpb", + "mugham", + "muiñeira", + "mulatós", + "muliza", + "murga", + "murga uruguaya", + "musette", + "music hall", + "música cebolla", + "música criolla", + "música de intervenção", + "musical", + "musique concrète", + "musique concrète instrumentale", + "muziki wa dansi", + "nagauta", + "narcocorrido", + "narodnozabavna glasba", + "nasheed", + "nashville sound", + "native american new age", + "nature sounds", + "natya sangeet", + "nederbeat", + "nederpop", + "neo kyma", + "neo soul", + "neo-acoustic", + "neo-medieval folk", + "neo-progressive rock", + "neo-psychedelia", + "neo-rockabilly", + "néo-trad", + "neo-traditional country", + "neoclassical dark wave", + "neoclassical metal", + "neoclassical new age", + "neoclassicism", + "neocrust", + "neofolk", + "neofolklore", + "neon pop punk", + "neoperreo", + "nerdcore", + "nerdcore techno", + "neue deutsche härte", + "neue deutsche welle", + "neurofunk", + "neurohop", + "new age", + "new beat", + "new complexity", + "new jack swing", + "new orleans blues", + "new orleans r&b", + "new rave", + "new romantic", + "new wave", + "ngoma", + "nhạc đỏ", + "nhạc vàng", + "night full-on", + "nightcore", + "nigun", + "nintendocore", + "nitzhonot", + "njuup", + "no melody trap", + "no wave", + "nocturne", + "noh", + "noise", + "noise pop", + "noise rock", + "noisecore", + "non-music", + "nortec", + "norteño", + "northern soul", + "nouveau zydeco", + "nova cançó", + "novelty piano", + "novo dub", + "nu disco", + "nu jazz", + "nu metal", + "nu skool breaks", + "nu style gabber", + "nueva canción", + "nueva canción chilena", + "nueva canción española", + "nueva cumbia chilena", + "nueva trova", + "nuevo cancionero", + "nuevo flamenco", + "nuevo tango", + "nwobhm", + "nyū myūjikku", + "oberek", + "occult rock", + "odissi classical", + "oi", + "old roman chant", + "old school death metal", + "old school hip hop", + "old-time", + "omutibo", + "onda nueva", + "ondō", + "onkyo", + "opera", + "opera buffa", + "opéra comique", + "opera semiseria", + "opera seria", + "opera-ballet", + "operatic pop", + "operetta", + "opm", + "oratorio", + "orchestral", + "orchestral jazz", + "orchestral song", + "organic house", + "oriental ballad", + "orkes gambus", + "orthodox pop", + "outlaw country", + "outsider house", + "overture", + "özgün müzik", + "p-funk", + "pachanga", + "pacific reggae", + "pagan black metal", + "pagan folk", + "pagodão", + "pagode", + "pagode romântico", + "paisley underground", + "palingsound", + "palm-wine", + "pansori", + "parang", + "partido alto", + "pasillo", + "pasodoble", + "payada", + "peak time techno", + "pep band", + "persian classical", + "persian pop", + "philly club", + "philly soul", + "phleng phuea chiwit", + "phonk", + "piano blues", + "piano rock", + "picopop", + "piedmont blues", + "pilón", + "pimba", + "pinpeat", + "pìobaireachd", + "pipe band music", + "piphat", + "piseiro", + "piyyut", + "pizzica", + "plainchant", + "plena", + "plugg", + "pluggnb", + "plunderphonics", + "poetry", + "polca criolla", + "political hip hop", + "polka", + "polka paraguaya", + "polonaise", + "pon-chak disco", + "pop", + "pop ghazal", + "pop kreatif", + "pop metal", + "pop minang", + "pop punk", + "pop raï", + "pop rap", + "pop rock", + "pop soul", + "pop yeh-yeh", + "porn groove", + "pornogrind", + "porro", + "post-bop", + "post-britpop", + "post-classical", + "post-grunge", + "post-hardcore", + "post-industrial", + "post-metal", + "post-minimalism", + "post-punk", + "post-punk revival", + "post-rock", + "powada", + "power electronics", + "power metal", + "power noise", + "power pop", + "power soca", + "powerstomp", + "powerviolence", + "praise & worship", + "prank calls", + "prelude", + "production music", + "progressive", + "progressive bluegrass", + "progressive breaks", + "progressive country", + "progressive electronic", + "progressive folk", + "progressive house", + "progressive metal", + "progressive pop", + "progressive psytrance", + "progressive rock", + "progressive trance", + "proto-punk", + "psybient", + "psychedelic", + "psychedelic folk", + "psychedelic pop", + "psychedelic rock", + "psychedelic soul", + "psychobilly", + "psycore", + "psystyle", + "psytrance", + "pub rock", + "punk", + "punk blues", + "punk rap", + "punk rock", + "punta", + "punto", + "purple sound", + "q-pop", + "qaraami", + "qasidah modern", + "qawwali", + "quan họ", + "queercore", + "quiet storm", + "quyi", + "r&b", + "rabiz", + "raga rock", + "ragga", + "ragga hip-hop", + "ragga jungle", + "raggacore", + "raggatek", + "ragtime", + "raï", + "ranchera", + "rap metal", + "rap rock", + "rapcore", + "rapso", + "rara", + "rasin", + "rasqueado cuiabano", + "rasteirinha", + "rautalanka", + "rave", + "raw punk", + "rawphoric", + "rawstyle", + "rebetiko", + "red dirt", + "red song", + "reductionism", + "reggae", + "reggae-pop", + "reggaeton", + "regional mexicano", + "renaissance", + "repente", + "requiem", + "revue", + "rhumba", + "riddim dubstep", + "rigsar", + "riot grrrl", + "ripsaw", + "ritual ambient", + "rizitika", + "rkt", + "rock", + "rock and roll", + "rock andaluz", + "rock andino", + "rock musical", + "rock opera", + "rock urbano", + "rockabilly", + "rocksteady", + "rōkyoku", + "rom kbach", + "romanian popcorn", + "romantic classical", + "romantische oper", + "roots reggae", + "roots rock", + "rumba", + "rumba catalana", + "rumba cubana", + "rumba flamenca", + "runo song", + "russian chanson", + "russian romance", + "ryūkōka", + "sacred harp", + "sacred steel", + "saeta", + "salegy", + "salsa", + "salsa choke", + "salsa dura", + "salsa romántica", + "saluang klasik", + "samba", + "samba de breque", + "samba de gafieira", + "samba de roda", + "samba de terreiro", + "samba soul", + "samba-canção", + "samba-choro", + "samba-enredo", + "samba-exaltação", + "samba-jazz", + "samba-joia", + "samba-reggae", + "samba-rock", + "sambalanço", + "sambass", + "sampledelia", + "sanjo", + "santé engagé", + "sarala gee", + "sardana", + "sasscore", + "sawt", + "saya afroboliviana", + "schlager", + "schottische", + "schranz", + "screamo", + "scrumpy and western", + "sea shanty", + "sean-nós", + "seapunk", + "séga", + "seggae", + "seguidilla", + "semba", + "semi-trot", + "serenade", + "serialism", + "sertanejo", + "sertanejo raiz", + "sertanejo romântico", + "sertanejo universitário", + "seto leelo", + "sevdalinka", + "sevillanas", + "shaabi", + "shabad kirtan", + "shan'ge", + "shangaan electro", + "shanto", + "shashmaqam", + "shatta", + "shibuya-kei", + "shidaiqu", + "shima-uta", + "shinkyoku", + "shoegaze", + "shōmyō", + "sierreño", + "sigidrigi", + "sigilkore", + "sinawi", + "sinfonia concertante", + "singeli", + "singer-songwriter", + "singspiel", + "ska", + "ska punk", + "skacore", + "skate punk", + "sketch comedy", + "skiffle", + "skiladiko", + "skinhead reggae", + "skullstep", + "skweee", + "slack-key guitar", + "slacker rock", + "slam death metal", + "slam poetry", + "slap house", + "slow waltz", + "slowcore", + "sludge metal", + "slushwave", + "smooth jazz", + "smooth soul", + "snap", + "soca", + "soft rock", + "sōkyoku", + "son calentano", + "son cubano", + "son huasteco", + "son istmeño", + "son jarocho", + "son montuno", + "sonata", + "songo", + "sonorism", + "sophisti-pop", + "soukous", + "soul", + "soul blues", + "soul jazz", + "sound art", + "sound collage", + "sound effects", + "sound poetry", + "southeast asian classical", + "southern gospel", + "southern hip hop", + "southern metal", + "southern rock", + "southern soul", + "sovietwave", + "space age pop", + "space ambient", + "space disco", + "space rock", + "space rock revival", + "spacesynth", + "spectralism", + "speech", + "speed garage", + "speed house", + "speed metal", + "speedcore", + "spirituals", + "splittercore", + "spoken word", + "spouge", + "standup comedy", + "steampunk", + "stenchcore", + "stochastic music", + "stoner metal", + "stoner rock", + "stornello", + "street punk", + "stride", + "sufi rock", + "sufiana kalam", + "sundanese pop", + "sungura", + "sunshine pop", + "suomisaundi", + "surf", + "surf punk", + "surf rock", + "sutartinės", + "swamp blues", + "swamp pop", + "swamp rock", + "swancore", + "swing", + "swing revival", + "symphonic black metal", + "symphonic metal", + "symphonic poem", + "symphonic prog", + "symphonic rock", + "symphony", + "synth funk", + "synth-pop", + "synthwave", + "taarab", + "tajaraste", + "takamba", + "talempong", + "talking blues", + "tallava", + "tamborera", + "tamborito", + "tammurriata", + "tango", + "tanjidor", + "tape music", + "taquirari", + "tarantella", + "tarraxinha", + "tassu", + "tchinkoumé", + "tearout", + "tech house", + "tech trance", + "technical death metal", + "technical thrash metal", + "techno", + "techno bass", + "techno kayō", + "technobanda", + "techstep", + "tecnobrega", + "tecnofunk", + "tecnorumba", + "teen pop", + "tejano", + "tembang cianjuran", + "terrorcore", + "tex-mex", + "texas blues", + "texas country", + "thai classical", + "third stream", + "third wave ska", + "thrash metal", + "thrashcore", + "thumri", + "tiento", + "timba", + "timbila", + "tin pan alley", + "tizita", + "toccata", + "tonada potosina", + "tonadilla", + "tondero", + "totalism", + "township bubblegum", + "township jive", + "tradi-modern", + "traditional black gospel", + "traditional country", + "traditional doom metal", + "traditional pop", + "tragédie en musique", + "trallalero", + "trampská hudba", + "trance", + "trance metal", + "trancestep", + "trap", + "trap edm", + "trap metal", + "trap shaabi", + "tread", + "tribal ambient", + "tribal guarachero", + "tribal house", + "trip hop", + "tropical house", + "tropical rock", + "tropicália", + "tropicanibalismo", + "tropipop", + "trot", + "trova", + "trova yucateca", + "truck driving country", + "tsapiky", + "tsonga disco", + "tsugaru-jamisen", + "tumba", + "tumba francesa", + "tumbélé", + "turbo-folk", + "turkish classical", + "turntablism", + "twee pop", + "twerk", + "uk drill", + "uk funky", + "uk garage", + "uk hardcore", + "uk street soul", + "uk82", + "underground hip hop", + "unyago", + "upopo", + "uptempo hardcore", + "urban cowboy", + "urtiin duu", + "urumi melam", + "utopian virtual", + "uzun hava", + "v-pop", + "vallenato", + "vals criollo", + "valsa brasileira", + "vanera", + "vaportrap", + "vaporwave", + "vaudeville", + "vaudeville blues", + "verbunkos", + "verismo", + "vietnamese bolero", + "vietnamese classical", + "viking metal", + "viking rock", + "vinahouse", + "visa", + "visual kei", + "vocal house", + "vocal jazz", + "vocal surf", + "vocal trance", + "volkstümliche musik", + "vude", + "waka", + "waltz", + "war metal", + "wassoulou", + "waulking song", + "wave", + "weightless", + "west coast breaks", + "west coast hip hop", + "west coast swing", + "western", + "western classical", + "western swing", + "whale song", + "whistling", + "white voice", + "winter synth", + "witch house", + "wong shadow", + "wonky", + "wonky techno", + "world fusion", + "xẩm", + "xote", + "xuc", + "yacht rock", + "yakousei", + "yaraví", + "yayue", + "yé-yé", + "yodeling", + "ytpmv", + "yu-mex", + "yue opera", + "yukar", + "zamacueca", + "zamba", + "zamrock", + "zarzuela", + "zeitoper", + "zenonesque", + "zeuhl", + "zeybek", + "zhongguo feng", + "ziglibithy", + "zinli", + "znamenny chant", + "zoblazo", + "zohioliin duu", + "zolo", + "zouglou", + "zouk", + "zouk love", + "zydeco", + }; + } +} \ No newline at end of file diff --git a/Zune.Net.SocialApi/appsettings.json b/Zune.Net.SocialApi/appsettings.json index 8383a8a..309b7d7 100644 --- a/Zune.Net.SocialApi/appsettings.json +++ b/Zune.Net.SocialApi/appsettings.json @@ -7,8 +7,6 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" diff --git a/Zune.Net.Tiles/appsettings.json b/Zune.Net.Tiles/appsettings.json index 10f68b8..cdcf7bb 100644 --- a/Zune.Net.Tiles/appsettings.json +++ b/Zune.Net.Tiles/appsettings.json @@ -2,7 +2,6 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" diff --git a/Zune.Net.Tuners/appsettings.json b/Zune.Net.Tuners/appsettings.json index 10f68b8..6a845cf 100644 --- a/Zune.Net.Tuners/appsettings.json +++ b/Zune.Net.Tuners/appsettings.json @@ -1,8 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Default": "Information" } }, "AllowedHosts": "*" From 8482bf3aece8e5a333ddabbb95af96dde2914292 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Mon, 5 Jun 2023 23:36:07 -0600 Subject: [PATCH 25/29] Apparently the HD also hits the training data endpoints? COOL! --- Zune.Net.Mix/Controllers/AlbumController.cs | 1 + Zune.Net.Mix/Controllers/ArtistController.cs | 2 +- Zune.Net.Mix/Controllers/TrackController.cs | 5 ++--- Zune.Net.Mix/Program.cs | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Zune.Net.Mix/Controllers/AlbumController.cs b/Zune.Net.Mix/Controllers/AlbumController.cs index 6026025..704c29e 100644 --- a/Zune.Net.Mix/Controllers/AlbumController.cs +++ b/Zune.Net.Mix/Controllers/AlbumController.cs @@ -4,6 +4,7 @@ namespace Zune.Net.Mix.Controllers { + [Route("/v3.0/model/album/")] [Route("/v4.0/{culture}/model/album/")] [Produces("application/xml")] public class AlbumController : Controller diff --git a/Zune.Net.Mix/Controllers/ArtistController.cs b/Zune.Net.Mix/Controllers/ArtistController.cs index c3a9883..faf743d 100644 --- a/Zune.Net.Mix/Controllers/ArtistController.cs +++ b/Zune.Net.Mix/Controllers/ArtistController.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using Atom.Xml; using Microsoft.AspNetCore.Mvc; using Zune.Net.Helpers; @@ -6,6 +5,7 @@ namespace Zune.Net.Mix.Controllers { + [Route("/v3.0/")] [Route("/v4.0/{culture}/")] public class ArtistController : Controller { diff --git a/Zune.Net.Mix/Controllers/TrackController.cs b/Zune.Net.Mix/Controllers/TrackController.cs index 1caf603..324aeb7 100644 --- a/Zune.Net.Mix/Controllers/TrackController.cs +++ b/Zune.Net.Mix/Controllers/TrackController.cs @@ -1,12 +1,11 @@ -using System; -using System.Threading.Tasks; -using Atom.Xml; +using Atom.Xml; using Microsoft.AspNetCore.Mvc; using Zune.Net.Helpers; using Zune.Xml.Catalog; namespace Zune.Net.Mix.Controllers { + [Route("/v3.0/track/")] [Route("/v4.0/{culture}/track/")] [Produces(Atom.Constants.ATOM_MIMETYPE)] public class TrackController : Controller diff --git a/Zune.Net.Mix/Program.cs b/Zune.Net.Mix/Program.cs index 5ae8dd6..3b43e4e 100644 --- a/Zune.Net.Mix/Program.cs +++ b/Zune.Net.Mix/Program.cs @@ -3,6 +3,7 @@ var builder = WebApplication.CreateBuilder(args); // Add services to the container. +builder.Services.AddLogging(x=>x.AddConsole()); builder.Services.AddControllers(); From c2f7bfc04ae6357e707120af41f7a8cd99baa399 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Mon, 5 Jun 2023 23:37:09 -0600 Subject: [PATCH 26/29] make things throw less --- .../Controllers/ImageController.cs | 73 ++++++++++--------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/Zune.Net.Catalog.Image/Controllers/ImageController.cs b/Zune.Net.Catalog.Image/Controllers/ImageController.cs index bf0d068..8d5cfff 100644 --- a/Zune.Net.Catalog.Image/Controllers/ImageController.cs +++ b/Zune.Net.Catalog.Image/Controllers/ImageController.cs @@ -62,7 +62,8 @@ public async Task Image(Guid id, bool resize = true, int width = var thisImage = images[idB]; imageUrl = thisImage.Value("uri"); } - } else + } + else { try { @@ -75,10 +76,11 @@ public async Task Image(Guid id, bool resize = true, int width = imageUrl = imageEntry.Url; } } - } catch {} + } + catch { } } - if(string.IsNullOrEmpty(imageUrl)) + if (string.IsNullOrEmpty(imageUrl)) { return await ArtistImage(id, "failoverFromPrimaryEndpoint", resize, width, contenttype); } @@ -113,39 +115,43 @@ public async Task ArtistImage(Guid id, string type, bool resize = private async Task ReturnResizedImageAsync(string imageUrl, bool resize, int width, string contenttype) { - var imgResponse = await imageUrl.GetAsync(); - - if (imgResponse.StatusCode != 200) + try { - NotFound(); - } + var imgResponse = await imageUrl.GetAsync(); - var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); - if (resize && image.Size.Width > width) - { - _logger.LogDebug("resizing"); - image.Mutate(x => x.Resize(width, 0)); - } + if (imgResponse.StatusCode != 200) + { + NotFound(); + } - using var stream = new MemoryStream(); + var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); + if (resize && image.Size.Width > width) + { + _logger.LogDebug("resizing"); + image.Mutate(x => x.Resize(width, 0)); + } - if (contenttype.Contains("jpeg")) - { - _logger.LogDebug("sending as jpg"); - image.Save(stream, new JpegEncoder()); - } - else if (contenttype.Contains("bmp")) - { - _logger.LogDebug("bmp"); - image.Save(stream, new BmpEncoder()); - } - else if (contenttype.Contains("png")) - { - _logger.LogDebug("sending as png"); - image.Save(stream, new PngEncoder()); - } + using var stream = new MemoryStream(); - return File(stream.ToArray(), contenttype); + if (contenttype.Contains("jpeg")) + { + _logger.LogDebug("sending as jpg"); + image.Save(stream, new JpegEncoder()); + } + else if (contenttype.Contains("bmp")) + { + _logger.LogDebug("bmp"); + image.Save(stream, new BmpEncoder()); + } + else if (contenttype.Contains("png")) + { + _logger.LogDebug("sending as png"); + image.Save(stream, new PngEncoder()); + } + + return File(stream.ToArray(), contenttype); + } + catch { return NotFound(); } } private static async Task GetImageUrlFromDCAsync(Guid id) @@ -163,10 +169,11 @@ private static string GetImageUrlFromCoverArchive(Guid id) try { albumId = MusicBrainz.GetAlbumByRecordingId(id).Id; - } catch + } + catch { } - if(string.IsNullOrEmpty(albumId)) + if (string.IsNullOrEmpty(albumId)) { albumId = id.ToString(); } From 9b53cf84bb64e4f7e269915df1c77b20bdb6adb2 Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Mon, 5 Jun 2023 23:39:25 -0600 Subject: [PATCH 27/29] implement caching and ratelimiting. --- nginx/nginx.conf | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 313ccac..6a8980a 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -6,6 +6,9 @@ http { sendfile on; + proxy_cache_path /tmp/cache levels=1:2 keys_zone=my_cache:1000m; + limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; + server{ listen 80; server_name toc.music.metaservices.microsoft.com metaservices.zune.net fai.music.metaservices.microsoft.com redir.metaservices.microsoft.com images.metaservices.microsoft.com; @@ -30,7 +33,16 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; - expires -1; + + proxy_buffering on; + + proxy_ignore_headers Expires Cache-Control X-Accel-Expires; + proxy_ignore_headers Set-Cookie; + + proxy_cache my_cache; + proxy_cache_valid 24h; + + limit_req zone=one; } } @@ -44,7 +56,14 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; - expires -1; + + proxy_buffering on; + + proxy_ignore_headers Expires Cache-Control X-Accel-Expires; + proxy_ignore_headers Set-Cookie; + + proxy_cache my_cache; + proxy_cache_valid 24h; } } @@ -59,7 +78,14 @@ http { location / { proxy_pass http://commerce; proxy_redirect off; - expires -1; + + proxy_buffering on; + + proxy_ignore_headers Expires Cache-Control X-Accel-Expires; + proxy_ignore_headers Set-Cookie; + + proxy_cache my_cache; + proxy_cache_valid 24h; } } @@ -91,7 +117,16 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; - expires -1; + + proxy_buffering on; + + proxy_ignore_headers Expires Cache-Control X-Accel-Expires; + proxy_ignore_headers Set-Cookie; + + proxy_cache my_cache; + proxy_cache_valid 14d; + + limit_req zone=one; } } From 498d1ccd2832c04fe8d682495e464d0e8929257d Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Mon, 5 Jun 2023 23:47:09 -0600 Subject: [PATCH 28/29] 10m is rookie numbers. --- nginx/nginx.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 6a8980a..2bafe19 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -7,7 +7,7 @@ http { sendfile on; proxy_cache_path /tmp/cache levels=1:2 keys_zone=my_cache:1000m; - limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; + limit_req_zone $binary_remote_addr zone=one:50m rate=1r/s; server{ listen 80; From 4b26d8693f448055698f2bd75841a36f50909baf Mon Sep 17 00:00:00 2001 From: Haley Grandle Date: Tue, 6 Jun 2023 09:29:05 -0600 Subject: [PATCH 29/29] do better. throttle musicbrainz requests or be banned. --- .../Controllers/ImageController.cs | 12 ++++++------ .../Controllers/StreamController.cs | 4 +--- Zune.Net.Mix/Program.cs | 4 ++++ Zune.Net.Shared/Helpers/MusicBrainz.cs | 8 +++++++- Zune.Net.Tuners/Resources/configuration.xml | 2 +- nginx/nginx.conf | 19 ++++++++----------- 6 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Zune.Net.Catalog.Image/Controllers/ImageController.cs b/Zune.Net.Catalog.Image/Controllers/ImageController.cs index 8d5cfff..984bec6 100644 --- a/Zune.Net.Catalog.Image/Controllers/ImageController.cs +++ b/Zune.Net.Catalog.Image/Controllers/ImageController.cs @@ -34,7 +34,7 @@ public ImageController(ZuneNetContext database, ILogger logger) } [HttpGet("image/{id}")] - public async Task Image(Guid id, bool resize = true, int width = 480, string contenttype = "image/jpeg") + public async Task Image(Guid id, int width, bool resize = false, string contenttype = "image/jpeg") { string? imageUrl = null; @@ -82,7 +82,7 @@ public async Task Image(Guid id, bool resize = true, int width = if (string.IsNullOrEmpty(imageUrl)) { - return await ArtistImage(id, "failoverFromPrimaryEndpoint", resize, width, contenttype); + return await ArtistImage(id, "failoverFromPrimaryEndpoint", width, resize, contenttype); } return await ReturnResizedImageAsync(imageUrl, resize, width, contenttype); @@ -91,7 +91,7 @@ public async Task Image(Guid id, bool resize = true, int width = // i.e. http://image.catalog.zune.net/v3.0/en-US/music/track/f32bb0ab-59d6-4620-b239-e86dc68647a4/albumImage?width=240&height=240&resize=true [HttpGet("music/{imageKind}/{id}/{type}")] - public async Task ArtistImage(Guid id, string type, bool resize = true, int width = 480, string contenttype = "image/jpeg") + public async Task ArtistImage(Guid id, string type, int width, bool resize = false, string contenttype = "image/jpeg") { _logger.LogDebug($"Fetching image type: '{type}', starting with DC"); // known types - deviceBackgroundImage, primaryImage, albumImage. @@ -113,7 +113,7 @@ public async Task ArtistImage(Guid id, string type, bool resize = return await ReturnResizedImageAsync(imageUrl, resize, width, contenttype); } - private async Task ReturnResizedImageAsync(string imageUrl, bool resize, int width, string contenttype) + private async Task ReturnResizedImageAsync(string imageUrl, bool resize, int? width, string contenttype) { try { @@ -125,10 +125,10 @@ private async Task ReturnResizedImageAsync(string imageUrl, bool } var image = await SixLabors.ImageSharp.Image.LoadAsync(await imgResponse.GetStreamAsync()); - if (resize && image.Size.Width > width) + if (width.HasValue && resize && image.Size.Width > width.Value) { _logger.LogDebug("resizing"); - image.Mutate(x => x.Resize(width, 0)); + image.Mutate(x => x.Resize(width.Value, 0)); } using var stream = new MemoryStream(); diff --git a/Zune.Net.Catalog/Controllers/StreamController.cs b/Zune.Net.Catalog/Controllers/StreamController.cs index d5d281a..27ecf34 100644 --- a/Zune.Net.Catalog/Controllers/StreamController.cs +++ b/Zune.Net.Catalog/Controllers/StreamController.cs @@ -1,11 +1,9 @@ -using Flurl; -using Flurl.Http; +using Flurl.Http; using MetaBrainz.MusicBrainz; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading.Tasks; using Zune.Net.Helpers; diff --git a/Zune.Net.Mix/Program.cs b/Zune.Net.Mix/Program.cs index 3b43e4e..b55d748 100644 --- a/Zune.Net.Mix/Program.cs +++ b/Zune.Net.Mix/Program.cs @@ -1,4 +1,5 @@ using Zune.Net; +using Zune.Net.Helpers; var builder = WebApplication.CreateBuilder(args); @@ -11,6 +12,9 @@ var app = builder.Build(); +// initialize so we have a cache to protect from overcalling +MusicBrainz.Initialize(app.Environment); + // Configure the HTTP request pipeline. // app.UseHttpLogging(); diff --git a/Zune.Net.Shared/Helpers/MusicBrainz.cs b/Zune.Net.Shared/Helpers/MusicBrainz.cs index b19fbc1..ab99ffb 100644 --- a/Zune.Net.Shared/Helpers/MusicBrainz.cs +++ b/Zune.Net.Shared/Helpers/MusicBrainz.cs @@ -13,10 +13,16 @@ public static partial class MusicBrainz public static void Initialize(IWebHostEnvironment env) { + // assume worst-case scenario, mix is getting hit at the same time as catalog. The + // frontent cache, nginx, will serve up cached results, but this interleave of 1.5s + // might be enought to avoid a race. + Query.DelayBetweenRequests = 1.5; + _query.ConfigureClientCreation(delegate { + // might be useful to put this as a shared resource between mix and cog var cachePath = System.IO.Path.Combine(env.ContentRootPath, "bin", "cache"); - var cacheTime = TimeSpan.FromMinutes(5); + var cacheTime = TimeSpan.FromHours(1); return new HttpClient(new CachedHttpClientHandler(cachePath, cacheTime)); }); } diff --git a/Zune.Net.Tuners/Resources/configuration.xml b/Zune.Net.Tuners/Resources/configuration.xml index 53a2a8e..d8ee0e2 100644 --- a/Zune.Net.Tuners/Resources/configuration.xml +++ b/Zune.Net.Tuners/Resources/configuration.xml @@ -2384,7 +2384,7 @@ mix - 0 + 100 diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 2bafe19..8b152f8 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -6,8 +6,9 @@ http { sendfile on; - proxy_cache_path /tmp/cache levels=1:2 keys_zone=my_cache:1000m; - limit_req_zone $binary_remote_addr zone=one:50m rate=1r/s; + proxy_cache_path /tmp/global levels=1:2 keys_zone=global_cache:1000m; + proxy_cache_path /tmp/mix levels=1:2 keys_zone=mix_cache:1000m; + proxy_cache_path /tmp/img levels=1:2 keys_zone=image_cache:1000m; server{ listen 80; @@ -39,10 +40,8 @@ http { proxy_ignore_headers Expires Cache-Control X-Accel-Expires; proxy_ignore_headers Set-Cookie; - proxy_cache my_cache; + proxy_cache global_cache; proxy_cache_valid 24h; - - limit_req zone=one; } } @@ -62,7 +61,7 @@ http { proxy_ignore_headers Expires Cache-Control X-Accel-Expires; proxy_ignore_headers Set-Cookie; - proxy_cache my_cache; + proxy_cache image_cache; proxy_cache_valid 24h; } } @@ -84,7 +83,7 @@ http { proxy_ignore_headers Expires Cache-Control X-Accel-Expires; proxy_ignore_headers Set-Cookie; - proxy_cache my_cache; + proxy_cache global_cache; proxy_cache_valid 24h; } } @@ -123,10 +122,8 @@ http { proxy_ignore_headers Expires Cache-Control X-Accel-Expires; proxy_ignore_headers Set-Cookie; - proxy_cache my_cache; - proxy_cache_valid 14d; - - limit_req zone=one; + proxy_cache mix_cache; + proxy_cache_valid 1d; } }