diff --git a/admin/app/admin/subscription_cohorts.rb b/admin/app/admin/subscription_cohorts.rb index 67cafeb3c..198b82b27 100644 --- a/admin/app/admin/subscription_cohorts.rb +++ b/admin/app/admin/subscription_cohorts.rb @@ -5,6 +5,25 @@ class Spacer ('%-5.5s' % percentage).gsub(' ', ' ') + '% - ' + val.to_s end end +=begin +select +count(id) as total, + count(first_downloaded_client_at) as downloaded, + count(first_ran_client_at) as ran_client, + count(first_certified_gear_at) as ftue, + count(first_music_session_at) as any_session, + count(first_real_music_session_at) as real_session, + count(first_good_music_session_at) as good_session, + count(first_invited_at) as invited, + count(first_friended_at) as friended, + count(first_subscribed_at) as subscribed +from users where users.created_at >= '2024-11-01' AND users.created_at < '2025-04-01' + +select first_name, last_name, email +from users where users.created_at >= '2024-11-01' AND users.created_at < '2025-04-01' + AND first_music_session_at is NULL; + +=end ActiveAdmin.register_page "Subscription Cohorts" do menu :parent => 'Reports' diff --git a/admin/app/models/cohort.rb b/admin/app/models/cohort.rb index 6f25edc28..60b769e0b 100644 --- a/admin/app/models/cohort.rb +++ b/admin/app/models/cohort.rb @@ -112,6 +112,19 @@ class Cohort < ActiveRecord::Base def self.cohort_users(cohort) User.where(created_at: cohort.group_start..cohort.group_end) end +=begin + SELECT played.user_id FROM + (SELECT user_id, COUNT(*) cnt FROM music_sessions_user_history msuh1 + WHERE + msuh1.created_at >= '2024-11-01' AND + msuh1.created_at <= '202' AND + EXTRACT(EPOCH FROM (msuh1.session_removed_at - msuh1.created_at)) >= 900 AND + (SELECT COUNT(*) FROM music_sessions_user_history msuh2 + WHERE msuh1.music_session_id = msuh2.music_session_id + ) > 1 + GROUP BY user_id + ) played +=end def _played_online_subquery(constraint) where = if constraint.is_a?(Range) diff --git a/jam-ui/src/components/public/JKDownloads.js b/jam-ui/src/components/public/JKDownloads.js index ac26254cd..58c4e5e6f 100644 --- a/jam-ui/src/components/public/JKDownloads.js +++ b/jam-ui/src/components/public/JKDownloads.js @@ -116,7 +116,7 @@ const JKDownloads = () => {
Download the free JamKazam app
{/*

{currentOS}

*/} -

+

You must use the JamKazam app to get into online sessions with other musicians. Our app also gives you the most feature-rich experience for JamTracks, recordings, live broadcasting, and other features. Click the button below to download the JamKazam app installer, then double click the installer to run it. For more detailed instructions,  {isMacOS ? see this help article : isWindows && see this help article}.

@@ -126,7 +126,7 @@ const JKDownloads = () => { Download JamKazam
-
+
Need a different version?
-
+
Need a different version?
    {availablePlatforms.filter(platform => platform !== selectedPlatform).map(platform => ( @@ -143,7 +143,7 @@ const JKDownloadsLegacy = () => {
-
+
System Requirements
{selectedPlatform === 'MacOSX' ? (

diff --git a/jam-ui/src/components/public/JKObsDownloads.js b/jam-ui/src/components/public/JKObsDownloads.js new file mode 100644 index 000000000..2a200912a --- /dev/null +++ b/jam-ui/src/components/public/JKObsDownloads.js @@ -0,0 +1,174 @@ +import React, { useState, useEffect } from 'react' +import { detectOS, isAppleSilicon } from '../../helpers/utils' +import {getClientDownloads, getObsPluginDownloads} from '../../helpers/rest' +import { Link } from 'react-router-dom' + +const DownloadButtonMacAppleMx = '/img/downloads/Download-Button-Obs-Mac-Apple-Mx.png' +const DownloadButtonMacIntel = '/img/downloads/Download-Button-Obs-Mac-Intel.png' +const DownloadButtonWindows = '/img/downloads/Download-Button-Obs-Windows.png' + +const JKObsDownloads = () => { + const [currentOS, setCurrentOS] = useState(null) + const [downloads, setDownloads] = useState({}) + + const availablePlatforms = React.useMemo(() => { + const keys = Object.keys(downloads) + //only show JamClientModern versions + const sortedStrings = keys.filter(key => key.startsWith('OBSPlugin')).map(key => key.substring('OBSPlugin/'.length)) + + return sortedStrings + }, [downloads]) + + const detectAndSetOS = () => { + let os = detectOS() + + if (os == "MacOSX") { + const silicon = isAppleSilicon(); + if (silicon == true) { + os = "MacOSX-M"; + } + else { + os = "MacOSX-Intel"; + } + } + if (!os) { + os = "Win32" + } + setCurrentOS(os) + } + + const fetchClientDownloads = () => { + getObsPluginDownloads() + .then(resp => { + if (resp.status === 200) { + return resp.json() + } + }).then(data => { + const platforms = Object.keys(data) + const downloadUris = {} + platforms.filter(p => p === 'OBSPlugin/MacOSX-M' || p === 'OBSPlugin/MacOSX-Intel' || p === 'OBSPlugin/Win32').forEach(platform => { + downloadUris[platform] = data[platform].uri + }) + setDownloads(downloadUris) + detectAndSetOS() + }) + .catch(err => { + console.error(err) + }) + } + + useEffect(() => { + fetchClientDownloads() + }, []) + + const downloadLink = React.useMemo(() => { + if (!currentOS) return null + return downloads[`OBSPlugin/${currentOS}`] + }, [currentOS]); + + const selectPlatform = (platform) => { + console.log('_DEBUG_ platform', platform) + setCurrentOS(platform) + } + + const downloadImageUrl = React.useMemo(() => { + if (!currentOS) return null + switch (currentOS) { + case 'MacOSX-M': + return DownloadButtonMacAppleMx + case 'MacOSX-Intel': + return DownloadButtonMacIntel + case 'Win32': + return DownloadButtonWindows + } + }, [currentOS]) + + const isMacOS = React.useMemo(() => { + if (!currentOS) return false + return currentOS.startsWith('MacOSX') + }, [currentOS]) + + + const isWindows = React.useMemo(() => { + if (!currentOS) return false + return currentOS === 'Win32' + }, [currentOS]) + + const platformDisplayName = (platform) => { + switch (platform) { + case 'MacOSX-M': + return 'JamKazam for Mac (Apple Silicon)' + case 'MacOSX-Intel': + return 'JamKazam for Mac (Intel)' + case 'Win32': + return 'JamKazam for Windows' + } + } + + const onClickPlatform = (event, platform) => { + event.preventDefault() + selectPlatform(platform) + } + + return ( + <> + {Object.keys(downloads).length > 0 && currentOS ? ( +

+
Download and install OBS Studio
+ {/*

{currentOS}

*/} +

+ +Before installing the JamKazam plugin for OBS, you must first download and install OBS Studio. + If you have not already done so, follow the instructions in this help article to download + and install OBS Studio version 30.2.3. Please note that you cannot use OBS video features with the legacy + JamKazam application - only with the current version of our app. +

+
Download and install JamKazam plugin for OBS
+

+ After installing OBS Studio as explained above, you'll need to install the JamKazam + plugin for OBS. This plugin lets JamKazam and OBS Studio work together. Click the button + below to download the plugin installer. When the download is complete, double click the + installer, and then follow the on-screen instructions to install the plugin. For more detailed + instructions, see this help article. +

+
+
+ + Download JamKazam + +
+
+
Need a different version?
+ +
+
+
+
System Requirements
+ {isMacOS ? ( +

+ To install the JamKazam plugin for OBS Studio:
+ - OBS Studio version 30.2.3 must already be installed on your computer
+ - Your Mac must be running macOS 10.15 (Catalina) or higher
+

+ ) : isWindows ? ( +

+ To install the JamKazam plugin for OBS Studio
+ - OBS Studio version 30.2.3 must already be installed on your computer
+ - Your computer must be running Windows 10 or 11, 64-bit (32-bit not supported) +

+ ) : null} + +
+
+ ) : ( +
Loading...
+ )} + + ) +} + +export default JKObsDownloads \ No newline at end of file diff --git a/jam-ui/src/helpers/rest.js b/jam-ui/src/helpers/rest.js index b5fab9ec7..b7a685a6d 100644 --- a/jam-ui/src/helpers/rest.js +++ b/jam-ui/src/helpers/rest.js @@ -685,6 +685,13 @@ export const getClientDownloads = () => { .catch(error => reject(error)); }); } +export const getObsPluginDownloads = () => { + return new Promise((resolve, reject) => { + apiFetch(`/artifacts/OBSPlugin`) + .then(response => resolve(response)) + .catch(error => reject(error)); + }); +} //paypalPlaceOrder export const paypalPlaceOrder = (options = {}) => { diff --git a/jam-ui/src/layouts/JKPublicRoutes.js b/jam-ui/src/layouts/JKPublicRoutes.js index 0740ee5c5..9b09ef6ba 100644 --- a/jam-ui/src/layouts/JKPublicRoutes.js +++ b/jam-ui/src/layouts/JKPublicRoutes.js @@ -10,6 +10,7 @@ import JKForum from '../components/public/help/JKForum'; import JKUnsubscribe from '../components/public/JKUnsubscribe'; import JKDownloads from '../components/public/JKDownloads'; import JKDownloadsLegacy from '../components/public/JKDownloadsLegacy'; +import JKObsDownloads from '../components/public/JKObsDownloads'; import JKJamTracksLanding from '../components/jamtracks/JKJamTracksLandingDev'; import JKJamTracksArtistLanding from '../components/jamtracks/JKJamTracksArtistLandingDev'; @@ -24,6 +25,7 @@ const JKPublicRoutes = ({ match: { url } }) => ( + diff --git a/ruby/lib/jam_ruby/models/artifact_update.rb b/ruby/lib/jam_ruby/models/artifact_update.rb index 518205ed0..b774580a4 100644 --- a/ruby/lib/jam_ruby/models/artifact_update.rb +++ b/ruby/lib/jam_ruby/models/artifact_update.rb @@ -4,6 +4,7 @@ module JamRuby DEFAULT_ENVIRONMENT = 'public' CLIENT_PREFIX = 'JamClient' CLIENT_PREFIX_MODERN = 'JamClientModern' + OBS_PLUGIN_PREFIX = 'OBSPlugin' PRODUCTS = [ "#{CLIENT_PREFIX}/Win32", @@ -13,7 +14,10 @@ module JamRuby "#{CLIENT_PREFIX_MODERN}/Win32", "#{CLIENT_PREFIX_MODERN}/MacOSX-Intel", "#{CLIENT_PREFIX_MODERN}/MacOSX-M", - "#{CLIENT_PREFIX_MODERN}/MacOSX" + "#{CLIENT_PREFIX_MODERN}/MacOSX", + "#{OBS_PLUGIN_PREFIX}/Win32", + "#{OBS_PLUGIN_PREFIX}/MacOSX-Intel", + "#{OBS_PLUGIN_PREFIX}/MacOSX-M", ] self.primary_key = 'id' diff --git a/web/app/controllers/artifacts_controller.rb b/web/app/controllers/artifacts_controller.rb index c1232b8db..173c29b15 100644 --- a/web/app/controllers/artifacts_controller.rb +++ b/web/app/controllers/artifacts_controller.rb @@ -16,7 +16,13 @@ class ArtifactsController < ApiController clients = ArtifactUpdate.where('product ilike ? and environment = ?', "JamClient/#{params[:type]}", ArtifactUpdate::DEFAULT_ENVIRONMENT).order(:product) end else - clients = ArtifactUpdate.where("product like '%JamClient%' and environment = '#{ArtifactUpdate::DEFAULT_ENVIRONMENT}'").order(:product) + if params[:type] + clients = ArtifactUpdate.where("product like ? and environment = '#{ArtifactUpdate::DEFAULT_ENVIRONMENT}'", "%" + params[:type] + "%").order(:product) + else + clients = ArtifactUpdate.where("product like '%JamClient%' and environment = '#{ArtifactUpdate::DEFAULT_ENVIRONMENT}'").order(:product) + + end + end if is_jamblaster && params[:serialno]