fixed migration sequence

Had duplicate migration sequences

Minor css changes and link rewrites to take you the specific item in the library overview for last watched items
This commit is contained in:
Thegan Govender
2023-04-16 20:55:05 +02:00
parent 5e867bf229
commit a87dd2c0d5
21 changed files with 221 additions and 33 deletions

View File

@@ -0,0 +1,87 @@
exports.up = function(knex) {
return knex.schema.raw(`
DROP FUNCTION IF EXISTS fs_last_library_activity(text);
CREATE OR REPLACE FUNCTION public.fs_last_library_activity(
libraryid text)
RETURNS TABLE("Id" text,"EpisodeId" text, "Name" text, "EpisodeName" text, "SeasonNumber" integer, "EpisodeNumber" integer, "PrimaryImageHash" text, "UserId" text, "UserName" text, "LastPlayed" interval)
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
ROWS 1000
AS $BODY$
BEGIN
RETURN QUERY
SELECT *
FROM (
SELECT DISTINCT ON (i."Name", e."Name")
i."Id",
a."EpisodeId",
i."Name",
e."Name" AS "EpisodeName",
CASE WHEN a."SeasonId" IS NOT NULL THEN s."IndexNumber" ELSE NULL END AS "SeasonNumber",
CASE WHEN a."SeasonId" IS NOT NULL THEN e."IndexNumber" ELSE NULL END AS "EpisodeNumber",
i."PrimaryImageHash",
a."UserId",
a."UserName",
(NOW() - a."ActivityDateInserted") as "LastPlayed"
FROM jf_playback_activity a
JOIN jf_library_items i ON i."Id" = a."NowPlayingItemId"
JOIN jf_libraries l ON i."ParentId" = l."Id"
LEFT JOIN jf_library_seasons s ON s."Id" = a."SeasonId"
LEFT JOIN jf_library_episodes e ON e."EpisodeId" = a."EpisodeId"
WHERE l."Id" = libraryid
ORDER BY i."Name", e."Name", a."ActivityDateInserted" DESC
) AS latest_distinct_rows
ORDER BY "LastPlayed"
LIMIT 15;
END;
$BODY$;
`).catch(function(error) {
console.error(error);
});
};
exports.down = function(knex) {
return knex.schema.raw(`
DROP FUNCTION IF EXISTS fs_last_library_activity(text);
CREATE OR REPLACE FUNCTION fs_last_library_activity(
libraryid text)
RETURNS TABLE("Id" text, "Name" text, "EpisodeName" text, "SeasonNumber" integer, "EpisodeNumber" integer, "PrimaryImageHash" text, "UserId" text, "UserName" text, "LastPlayed" interval)
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
ROWS 1000
AS $BODY$
BEGIN
RETURN QUERY
SELECT *
FROM (
SELECT DISTINCT ON (i."Name", e."Name")
i."Id",
i."Name",
e."Name" AS "EpisodeName",
CASE WHEN a."SeasonId" IS NOT NULL THEN s."IndexNumber" ELSE NULL END AS "SeasonNumber",
CASE WHEN a."SeasonId" IS NOT NULL THEN e."IndexNumber" ELSE NULL END AS "EpisodeNumber",
i."PrimaryImageHash",
a."UserId",
a."UserName",
(NOW() - a."ActivityDateInserted") as "LastPlayed"
FROM jf_playback_activity a
JOIN jf_library_items i ON i."Id" = a."NowPlayingItemId"
JOIN jf_libraries l ON i."ParentId" = l."Id"
LEFT JOIN jf_library_seasons s ON s."Id" = a."SeasonId"
LEFT JOIN jf_library_episodes e ON e."EpisodeId" = a."EpisodeId"
WHERE l."Id" = libraryid
ORDER BY i."Name", e."Name", a."ActivityDateInserted" DESC
) AS latest_distinct_rows
ORDER BY "LastPlayed"
LIMIT 15;
END;
$BODY$;
`);
};

View File

@@ -0,0 +1,96 @@
exports.up = function(knex) {
return knex.raw(`
DROP FUNCTION IF EXISTS fs_last_user_activity(text);
CREATE OR REPLACE FUNCTION public.fs_last_user_activity(
userid text)
RETURNS TABLE("Id" text,"EpisodeId" text, "Name" text, "EpisodeName" text, "SeasonNumber" integer, "EpisodeNumber" integer, "PrimaryImageHash" text, "UserId" text, "UserName" text, "LastPlayed" interval)
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
ROWS 1000
AS $BODY$
BEGIN
RETURN QUERY
SELECT *
FROM (
SELECT DISTINCT ON (i."Name", e."Name")
i."Id",
a."EpisodeId",
i."Name",
e."Name" AS "EpisodeName",
CASE WHEN a."SeasonId" IS NOT NULL THEN s."IndexNumber" ELSE NULL END AS "SeasonNumber",
CASE WHEN a."SeasonId" IS NOT NULL THEN e."IndexNumber" ELSE NULL END AS "EpisodeNumber",
i."PrimaryImageHash",
a."UserId",
a."UserName",
(NOW() - a."ActivityDateInserted") as "LastPlayed"
FROM jf_playback_activity a
JOIN jf_library_items i ON i."Id" = a."NowPlayingItemId"
LEFT JOIN jf_library_seasons s ON s."Id" = a."SeasonId"
LEFT JOIN jf_library_episodes e ON e."EpisodeId" = a."EpisodeId"
WHERE a."UserId" = userid
) AS latest_distinct_rows
ORDER BY "LastPlayed";
END;
$BODY$;
ALTER FUNCTION fs_last_user_activity(text)
OWNER TO ${process.env.POSTGRES_USER};
`).catch(function(error) {
console.error(error);
});
};
exports.down = function(knex) {
return knex.raw(`
DROP FUNCTION IF EXISTS fs_last_user_activity(text);
CREATE OR REPLACE FUNCTION fs_last_user_activity(
userid text
)
RETURNS TABLE(
"Id" text,
"Name" text,
"EpisodeName" text,
"SeasonNumber" integer,
"EpisodeNumber" integer,
"PrimaryImageHash" text,
"UserId" text,
"UserName" text,
"LastPlayed" interval
)
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
ROWS 1000
AS $BODY$
BEGIN
RETURN QUERY
SELECT *
FROM (
SELECT DISTINCT ON (i."Name", e."Name")
i."Id",
i."Name",
e."Name" AS "EpisodeName",
CASE WHEN a."SeasonId" IS NOT NULL THEN s."IndexNumber" ELSE NULL END AS "SeasonNumber",
CASE WHEN a."SeasonId" IS NOT NULL THEN e."IndexNumber" ELSE NULL END AS "EpisodeNumber",
i."PrimaryImageHash",
a."UserId",
a."UserName",
(NOW() - a."ActivityDateInserted") as "LastPlayed"
FROM jf_playback_activity a
JOIN jf_library_items i ON i."Id" = a."NowPlayingItemId"
LEFT JOIN jf_library_seasons s ON s."Id" = a."SeasonId"
LEFT JOIN jf_library_episodes e ON e."EpisodeId" = a."EpisodeId"
WHERE a."UserId" = userid
) AS latest_distinct_rows
ORDER BY "LastPlayed";
END;
$BODY$;
ALTER FUNCTION fs_last_user_activity(text)
OWNER TO ${process.env.POSTGRES_USER};
`);
};

View File

@@ -33,7 +33,7 @@ function LastWatchedCard(props) {
const [loaded, setLoaded] = useState(false);
return (
<div className="last-card">
<Link to={`/item/${props.data.Id}`}>
<Link to={`/item/${props.data.EpisodeId||props.data.Id}`}>
<div className="last-card-banner">
{loaded ? null : <Blurhash hash={props.data.PrimaryImageHash} width={'100%'} height={'100%'}/>}
<img

View File

@@ -33,7 +33,6 @@ useEffect(() => {
if(config){
setRefresh(true);
try {
console.log(Id);
const itemData = await axios.post(`/api/getItemDetails`, {
Id: Id
}, {
@@ -64,7 +63,7 @@ useEffect(() => {
return () => clearInterval(intervalId);
}, [config, Id]);
console.log(data);
if(!data)

View File

@@ -75,7 +75,6 @@ function ItemDetails(props) {
</h1>
<div className="my-3">
{props.data.CommunityRating ? <p style={{color:"lightgrey", fontSize:"0.8em", fontStyle:"italic"}}>Community Rating: {props.data.CommunityRating}</p> :<></>}
{props.data.Type==="Episode"? <p><Link to={`/item/${props.data.SeasonId}`} className="fw-bold">{props.data.SeasonName}</Link> Episode {props.data.IndexNumber} - {props.data.Name}</p> : <></> }
{props.data.Type==="Season"? <p>{props.data.Name}</p> : <></> }
{props.data.FileName ? <p style={{color:"lightgrey"}} className="fst-italic fs-6">File Name: {props.data.FileName}</p> :<></>}

View File

@@ -14,7 +14,7 @@ function LibraryStatComponent(props) {
};
const cardBgStyle = {
backdropFilter: 'blur(10px)',
backdropFilter: 'blur(5px)',
backgroundColor: 'rgb(0, 0, 0, 0.6)',
height:'100%',
};

View File

@@ -39,7 +39,7 @@ function sessionCard(props) {
};
const cardBgStyle = {
backdropFilter: 'blur(10px)',
backdropFilter: 'blur(5px)',
backgroundColor: 'rgb(0, 0, 0, 0.6)',
height:'100%',
};

View File

@@ -17,11 +17,11 @@ function ItemStatComponent(props) {
const cardStyle = {
backgroundImage: `url(${props.base_url}/Items/${props.data[0].Id}/Images/Backdrop/?fillWidth=300&quality=10), linear-gradient(to right, #00A4DC, #AA5CC3)`,
height:'100%',
backgroundSize: 'contain',
backgroundSize: 'cover',
};
const cardBgStyle = {
backdropFilter: 'blur(10px)',
backdropFilter: 'blur(5px)',
backgroundColor: 'rgb(0, 0, 0, 0.6)',
height:'100%',
};

View File

@@ -5,7 +5,7 @@
grid-auto-rows: 120px;
background-color: rgb(100, 100, 100,0.2);
padding: 20px;
border-radius: 4px;
border-radius: 8px;
font-size: 1.3em;
}

View File

@@ -4,7 +4,7 @@
background-color: rgb(100, 100, 100,0.2);
padding: 20px;
margin: 20px 0;
border-radius: 4px;
border-radius: 8px;
}
.item-name
@@ -16,7 +16,7 @@
.item-image
{
max-width: 200px;
border-radius: 4px;
border-radius: 8px;
object-fit: cover;
}

View File

@@ -4,7 +4,7 @@
overflow-x: auto;
background-color: rgb(100, 100, 100,0.2);
padding: 20px;
border-radius: 4px;
border-radius: 8px;
color: white;
margin-bottom: 20px;
min-height: 300px;
@@ -41,7 +41,7 @@
margin-right: 20px;
width: 150px;
border-radius: 4px;
border-radius: 8px;
}
@@ -70,7 +70,7 @@
width: 100%;
height: 100%;
border-radius: 4px 4px 0px 0px;
border-radius: 8px 8px 0px 0px;
}

View File

@@ -4,7 +4,7 @@
grid-template-columns: repeat(auto-fit, minmax(320px, 520px));
grid-auto-rows: 200px;/* max-width+offset so 215 + 20*/
border-radius: 4px;
border-radius: 8px;
/* margin-right: 20px; */
}
@@ -106,7 +106,7 @@
.library{
width: 100%;
padding: 5px 20px;
backdrop-filter: blur(4px);
backdrop-filter: blur(8px);
background-color: rgb(0, 0, 0, 0.6);
}

View File

@@ -9,7 +9,14 @@
align-items: center;
z-index: 9999;
background-color: #1e1c22;
transition: opacity 800ms ease-in;
opacity: 1;
}
.loading::before
{
opacity: 0;
}
.component-loading {
height: inherit;

View File

@@ -4,7 +4,7 @@
grid-auto-rows: 340px;/* max-width+offset so 215 + 20*/
background-color: rgba(0,0,0,0.5);
padding: 20px;
border-radius: 4px;
border-radius: 8px;
margin-right: 20px;
color: white;
@@ -23,7 +23,7 @@
height: 320px;
width: 185px;
border-radius: 4px;
border-radius: 8px;
/* Add a third row that takes up remaining space */
@@ -36,7 +36,7 @@
background-size: cover;
background-repeat: no-repeat;
background-position: center top;
border-radius: 4px 4px 0px 0px;
border-radius: 8px 8px 0px 0px;
}
@@ -46,7 +46,7 @@
width: 100%;
height: 30%;
position: relative;
/* margin: 4px; */
/* margin: 8px; */
/* background-color: #f71b1b; */
}

View File

@@ -22,7 +22,7 @@
/* margin-bottom: 10px; */
background-size: cover;
border-radius: 4px 4px 0px 4px;
border-radius: 8px 8px 0px 8px;
display: grid;
grid-template-columns: auto 1fr;
@@ -36,7 +36,7 @@
grid-column: 1/3; */
height: 5px;
background-color: #101010 !important;
border-radius: 0px 0px 4px 4px;
border-radius: 0px 0px 8px 8px;
}
@@ -45,7 +45,7 @@
height: 100%;
background-color: #00A4DC;
transition: width 0.2s ease-in-out;
border-radius: 0px 0px 0px 4px;
border-radius: 0px 0px 0px 8px;
}
.card-banner {
@@ -66,12 +66,12 @@
grid-column: 2 / 3;
backdrop-filter: blur(1px);
background-color: rgb(0, 0, 0, 0.6);
border-radius: 0px 4px 0px 0px;
border-radius: 0px 8px 0px 0px;
}
.card-banner-image {
border-radius: 4px 0px 0px 0px;
border-radius: 8px 0px 0px 0px;
max-height: inherit;
/* box-shadow: 0 0 20px 5px rgba(0, 0, 0, 0.8); */
}

View File

@@ -4,7 +4,7 @@
grid-template-columns: repeat(auto-fit, minmax(320px, 520px));
grid-auto-rows: 200px;/* max-width+offset so 215 + 20*/
/* margin-right: 20px; */
margin-top: 4px;
margin-top: 8px;
}
.stat-card{
border: 0 !important;
@@ -12,7 +12,7 @@
color: white;
max-width: 500px;
max-height: 180px;
border-radius: 4px !important;
border-radius: 8px !important;
}
.stat-card-banner
@@ -71,7 +71,7 @@
color: white;
display: flex;
background-color: rgb(100, 100, 100,0.3);
border-radius: 4px;
border-radius: 8px;
font-size: 1.2em;
align-self: flex-end;
justify-content: space-evenly;

View File

@@ -11,7 +11,7 @@
background-color:rgba(100,100,100,0.2);
padding:10px;
border-radius:4px;
border-radius:8px;
/* text-align: center; */
}

View File

@@ -4,7 +4,7 @@
background-color: rgb(100, 100, 100,0.2);
padding: 20px;
margin: 20px 0;
border-radius: 4px;
border-radius: 8px;
display: flex;
align-items: center;

View File

@@ -124,7 +124,7 @@ td:first-child {
color: white;
display: flex;
background-color: rgb(100, 100, 100,0.3);
border-radius: 4px;
border-radius: 8px;
font-size: 1.2em;
align-self: flex-end;
justify-content: space-between;
@@ -136,7 +136,7 @@ td:first-child {
height: 35px;
outline: none;
border: none;
border-radius: 4px;
border-radius: 8px;
background-color: rgb(255, 255, 255, 0.1);
color:white;
font-size: 1em;

View File

@@ -6,7 +6,7 @@
margin-top: 20px;
padding: 10px;
margin-right: 10px;
border-radius: 4px;
border-radius: 8px;
}
.console-message {