mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
@@ -64,6 +64,8 @@ input SceneFilterType {
|
|||||||
rating: IntCriterionInput
|
rating: IntCriterionInput
|
||||||
"""Filter by resolution"""
|
"""Filter by resolution"""
|
||||||
resolution: ResolutionEnum
|
resolution: ResolutionEnum
|
||||||
|
"""Filter by duration (in seconds)"""
|
||||||
|
duration: IntCriterionInput
|
||||||
"""Filter to only include scenes which have markers. `true` or `false`"""
|
"""Filter to only include scenes which have markers. `true` or `false`"""
|
||||||
has_markers: String
|
has_markers: String
|
||||||
"""Filter to only include scenes missing this property"""
|
"""Filter to only include scenes missing this property"""
|
||||||
|
|||||||
@@ -171,13 +171,19 @@ func (qb *SceneQueryBuilder) Query(sceneFilter *SceneFilterType, findFilter *Fin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if rating := sceneFilter.Rating; rating != nil {
|
if rating := sceneFilter.Rating; rating != nil {
|
||||||
clause, count := getIntCriterionWhereClause("rating", *sceneFilter.Rating)
|
clause, count := getIntCriterionWhereClause("scenes.rating", *sceneFilter.Rating)
|
||||||
whereClauses = append(whereClauses, clause)
|
whereClauses = append(whereClauses, clause)
|
||||||
if count == 1 {
|
if count == 1 {
|
||||||
args = append(args, sceneFilter.Rating.Value)
|
args = append(args, sceneFilter.Rating.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if durationFilter := sceneFilter.Duration; durationFilter != nil {
|
||||||
|
clause, thisArgs := getDurationWhereClause(*durationFilter)
|
||||||
|
whereClauses = append(whereClauses, clause)
|
||||||
|
args = append(args, thisArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
if resolutionFilter := sceneFilter.Resolution; resolutionFilter != nil {
|
if resolutionFilter := sceneFilter.Resolution; resolutionFilter != nil {
|
||||||
if resolution := resolutionFilter.String(); resolutionFilter.IsValid() {
|
if resolution := resolutionFilter.String(); resolutionFilter.IsValid() {
|
||||||
switch resolution {
|
switch resolution {
|
||||||
@@ -270,6 +276,34 @@ func appendClause(clauses []string, clause string) []string {
|
|||||||
return clauses
|
return clauses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDurationWhereClause(durationFilter IntCriterionInput) (string, []interface{}) {
|
||||||
|
// special case for duration. We accept duration as seconds as int but the
|
||||||
|
// field is floating point. Change the equals filter to return a range
|
||||||
|
// between x and x + 1
|
||||||
|
// likewise, not equals needs to be duration < x OR duration >= x
|
||||||
|
var clause string
|
||||||
|
args := []interface{}{}
|
||||||
|
|
||||||
|
value := durationFilter.Value
|
||||||
|
if durationFilter.Modifier == CriterionModifierEquals {
|
||||||
|
clause = "scenes.duration >= ? AND scenes.duration < ?"
|
||||||
|
args = append(args, value)
|
||||||
|
args = append(args, value+1)
|
||||||
|
} else if durationFilter.Modifier == CriterionModifierNotEquals {
|
||||||
|
clause = "(scenes.duration < ? OR scenes.duration >= ?)"
|
||||||
|
args = append(args, value)
|
||||||
|
args = append(args, value+1)
|
||||||
|
} else {
|
||||||
|
var count int
|
||||||
|
clause, count = getIntCriterionWhereClause("scenes.duration", durationFilter)
|
||||||
|
if count == 1 {
|
||||||
|
args = append(args, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clause, args
|
||||||
|
}
|
||||||
|
|
||||||
// returns where clause and having clause
|
// returns where clause and having clause
|
||||||
func getMultiCriterionClause(table string, joinTable string, joinTableField string, criterion *MultiCriterionInput) (string, string) {
|
func getMultiCriterionClause(table string, joinTable string, joinTableField string, criterion *MultiCriterionInput) (string, string) {
|
||||||
whereClause := ""
|
whereClause := ""
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { FunctionComponent, useState, useEffect } from "react";
|
import React, { FunctionComponent, useState, useEffect } from "react";
|
||||||
import { InputGroup, ButtonGroup, Button, IInputGroupProps, HTMLInputProps, ControlGroup } from "@blueprintjs/core";
|
import { InputGroup, ButtonGroup, Button, IInputGroupProps, HTMLInputProps, ControlGroup } from "@blueprintjs/core";
|
||||||
import { TextUtils } from "../../utils/text";
|
import { DurationUtils } from "../../utils/duration";
|
||||||
import { FIXED, NUMERIC_INPUT } from "@blueprintjs/core/lib/esm/common/classes";
|
import { FIXED, NUMERIC_INPUT } from "@blueprintjs/core/lib/esm/common/classes";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@@ -11,65 +11,20 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DurationInput: FunctionComponent<HTMLInputProps & IProps> = (props: IProps) => {
|
export const DurationInput: FunctionComponent<HTMLInputProps & IProps> = (props: IProps) => {
|
||||||
const [value, setValue] = useState<string>(secondsToString(props.numericValue));
|
const [value, setValue] = useState<string>(DurationUtils.secondsToString(props.numericValue));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(secondsToString(props.numericValue));
|
setValue(DurationUtils.secondsToString(props.numericValue));
|
||||||
}, [props.numericValue]);
|
}, [props.numericValue]);
|
||||||
|
|
||||||
function secondsToString(seconds : number) {
|
|
||||||
let ret = TextUtils.secondsToTimestamp(seconds);
|
|
||||||
|
|
||||||
if (ret.startsWith("00:")) {
|
|
||||||
ret = ret.substr(3);
|
|
||||||
|
|
||||||
if (ret.startsWith("0")) {
|
|
||||||
ret = ret.substr(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringToSeconds(v : string) {
|
|
||||||
if (!v) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let splits = v.split(":");
|
|
||||||
|
|
||||||
if (splits.length > 3) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let seconds = 0;
|
|
||||||
let factor = 1;
|
|
||||||
while(splits.length > 0) {
|
|
||||||
let thisSplit = splits.pop();
|
|
||||||
if (thisSplit == undefined) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let thisInt = parseInt(thisSplit, 10);
|
|
||||||
if (isNaN(thisInt)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
seconds += factor * thisInt;
|
|
||||||
factor *= 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
return seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
function increment() {
|
function increment() {
|
||||||
let seconds = stringToSeconds(value);
|
let seconds = DurationUtils.stringToSeconds(value);
|
||||||
seconds += 1;
|
seconds += 1;
|
||||||
props.onValueChange(seconds);
|
props.onValueChange(seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
function decrement() {
|
function decrement() {
|
||||||
let seconds = stringToSeconds(value);
|
let seconds = DurationUtils.stringToSeconds(value);
|
||||||
seconds -= 1;
|
seconds -= 1;
|
||||||
props.onValueChange(seconds);
|
props.onValueChange(seconds);
|
||||||
}
|
}
|
||||||
@@ -117,7 +72,7 @@ export const DurationInput: FunctionComponent<HTMLInputProps & IProps> = (props:
|
|||||||
disabled={props.disabled}
|
disabled={props.disabled}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e : any) => setValue(e.target.value)}
|
onChange={(e : any) => setValue(e.target.value)}
|
||||||
onBlur={() => props.onValueChange(stringToSeconds(value))}
|
onBlur={() => props.onValueChange(DurationUtils.stringToSeconds(value))}
|
||||||
placeholder="hh:mm:ss"
|
placeholder="hh:mm:ss"
|
||||||
rightElement={maybeRenderReset()}
|
rightElement={maybeRenderReset()}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import _ from "lodash";
|
|||||||
import React, { FunctionComponent, useEffect, useRef, useState } from "react";
|
import React, { FunctionComponent, useEffect, useRef, useState } from "react";
|
||||||
import { isArray } from "util";
|
import { isArray } from "util";
|
||||||
import { CriterionModifier } from "../../core/generated-graphql";
|
import { CriterionModifier } from "../../core/generated-graphql";
|
||||||
import { Criterion, CriterionType } from "../../models/list-filter/criteria/criterion";
|
import { Criterion, CriterionType, DurationCriterion } from "../../models/list-filter/criteria/criterion";
|
||||||
import { NoneCriterion } from "../../models/list-filter/criteria/none";
|
import { NoneCriterion } from "../../models/list-filter/criteria/none";
|
||||||
import { PerformersCriterion } from "../../models/list-filter/criteria/performers";
|
import { PerformersCriterion } from "../../models/list-filter/criteria/performers";
|
||||||
import { StudiosCriterion } from "../../models/list-filter/criteria/studios";
|
import { StudiosCriterion } from "../../models/list-filter/criteria/studios";
|
||||||
@@ -19,6 +19,7 @@ import { TagsCriterion } from "../../models/list-filter/criteria/tags";
|
|||||||
import { makeCriteria } from "../../models/list-filter/criteria/utils";
|
import { makeCriteria } from "../../models/list-filter/criteria/utils";
|
||||||
import { ListFilterModel } from "../../models/list-filter/filter";
|
import { ListFilterModel } from "../../models/list-filter/filter";
|
||||||
import { FilterMultiSelect } from "../select/FilterMultiSelect";
|
import { FilterMultiSelect } from "../select/FilterMultiSelect";
|
||||||
|
import { DurationInput } from "../Shared/DurationInput";
|
||||||
|
|
||||||
interface IAddFilterProps {
|
interface IAddFilterProps {
|
||||||
onAddCriterion: (criterion: Criterion, oldId?: string) => void;
|
onAddCriterion: (criterion: Criterion, oldId?: string) => void;
|
||||||
@@ -64,6 +65,11 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||||||
valueStage.current = event.target.value;
|
valueStage.current = event.target.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onChangedDuration(valueAsNumber: number) {
|
||||||
|
valueStage.current = valueAsNumber;
|
||||||
|
onBlurInput();
|
||||||
|
}
|
||||||
|
|
||||||
function onBlurInput() {
|
function onBlurInput() {
|
||||||
const newCriterion = _.cloneDeep(criterion);
|
const newCriterion = _.cloneDeep(criterion);
|
||||||
newCriterion.value = valueStage.current;
|
newCriterion.value = valueStage.current;
|
||||||
@@ -148,6 +154,14 @@ export const AddFilter: FunctionComponent<IAddFilterProps> = (props: IAddFilterP
|
|||||||
defaultValue={criterion.value}
|
defaultValue={criterion.value}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
} else if (criterion instanceof DurationCriterion) {
|
||||||
|
// render duration control
|
||||||
|
return (
|
||||||
|
<DurationInput
|
||||||
|
numericValue={criterion.value ? criterion.value : 0}
|
||||||
|
onValueChange={onChangedDuration}
|
||||||
|
/>
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<InputGroup
|
<InputGroup
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { isArray } from "util";
|
import { isArray } from "util";
|
||||||
import { CriterionModifier } from "../../../core/generated-graphql";
|
import { CriterionModifier } from "../../../core/generated-graphql";
|
||||||
import { ILabeledId, ILabeledValue } from "../types";
|
import { ILabeledId, ILabeledValue } from "../types";
|
||||||
|
import { DurationUtils } from "../../../utils/duration";
|
||||||
|
|
||||||
export type CriterionType =
|
export type CriterionType =
|
||||||
"none" |
|
"none" |
|
||||||
"rating" |
|
"rating" |
|
||||||
"resolution" |
|
"resolution" |
|
||||||
|
"duration" |
|
||||||
"favorite" |
|
"favorite" |
|
||||||
"hasMarkers" |
|
"hasMarkers" |
|
||||||
"isMissing" |
|
"isMissing" |
|
||||||
@@ -32,6 +34,7 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||||||
case "none": return "None";
|
case "none": return "None";
|
||||||
case "rating": return "Rating";
|
case "rating": return "Rating";
|
||||||
case "resolution": return "Resolution";
|
case "resolution": return "Resolution";
|
||||||
|
case "duration": return "Duration";
|
||||||
case "favorite": return "Favorite";
|
case "favorite": return "Favorite";
|
||||||
case "hasMarkers": return "Has Markers";
|
case "hasMarkers": return "Has Markers";
|
||||||
case "isMissing": return "Is Missing";
|
case "isMissing": return "Is Missing";
|
||||||
@@ -91,10 +94,18 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||||||
default: modifierString = "";
|
default: modifierString = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let valueString = "";
|
||||||
|
|
||||||
|
if (this.modifier !== CriterionModifier.IsNull && this.modifier !== CriterionModifier.NotNull) {
|
||||||
|
valueString = this.getLabelValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${Criterion.getLabel(this.type)} ${modifierString} ${valueString}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLabelValue() {
|
||||||
let valueString: string;
|
let valueString: string;
|
||||||
if (this.modifier === CriterionModifier.IsNull || this.modifier === CriterionModifier.NotNull) {
|
if (isArray(this.value) && this.value.length > 0) {
|
||||||
valueString = "";
|
|
||||||
} else if (isArray(this.value) && this.value.length > 0) {
|
|
||||||
let items = this.value;
|
let items = this.value;
|
||||||
if ((this.value as ILabeledId[])[0].label) {
|
if ((this.value as ILabeledId[])[0].label) {
|
||||||
items = this.value.map((item) => item.label) as any;
|
items = this.value.map((item) => item.label) as any;
|
||||||
@@ -106,7 +117,7 @@ export abstract class Criterion<Option = any, Value = any> {
|
|||||||
valueString = this.value.toString();
|
valueString = this.value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${Criterion.getLabel(this.type)} ${modifierString} ${valueString}`;
|
return valueString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getId(): string {
|
public getId(): string {
|
||||||
@@ -195,3 +206,34 @@ export class NumberCriterion extends Criterion<number, number> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class DurationCriterion extends Criterion<number, number> {
|
||||||
|
public type: CriterionType;
|
||||||
|
public parameterName: string;
|
||||||
|
public modifier = CriterionModifier.Equals;
|
||||||
|
public modifierOptions = [
|
||||||
|
Criterion.getModifierOption(CriterionModifier.Equals),
|
||||||
|
Criterion.getModifierOption(CriterionModifier.NotEquals),
|
||||||
|
Criterion.getModifierOption(CriterionModifier.GreaterThan),
|
||||||
|
Criterion.getModifierOption(CriterionModifier.LessThan),
|
||||||
|
];
|
||||||
|
public options: number[] | undefined;
|
||||||
|
public value: number = 0;
|
||||||
|
|
||||||
|
constructor(type : CriterionType, parameterName?: string, options? : number[]) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.type = type;
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
if (!!parameterName) {
|
||||||
|
this.parameterName = parameterName;
|
||||||
|
} else {
|
||||||
|
this.parameterName = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLabelValue() {
|
||||||
|
return DurationUtils.secondsToString(this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
CriterionModifier,
|
CriterionModifier,
|
||||||
} from "../../../core/generated-graphql";
|
} from "../../../core/generated-graphql";
|
||||||
import { Criterion, CriterionType, StringCriterion, NumberCriterion } from "./criterion";
|
import { Criterion, CriterionType, StringCriterion, NumberCriterion, DurationCriterion } from "./criterion";
|
||||||
import { FavoriteCriterion } from "./favorite";
|
import { FavoriteCriterion } from "./favorite";
|
||||||
import { HasMarkersCriterion } from "./has-markers";
|
import { HasMarkersCriterion } from "./has-markers";
|
||||||
import { IsMissingCriterion } from "./is-missing";
|
import { IsMissingCriterion } from "./is-missing";
|
||||||
@@ -17,6 +17,7 @@ export function makeCriteria(type: CriterionType = "none") {
|
|||||||
case "none": return new NoneCriterion();
|
case "none": return new NoneCriterion();
|
||||||
case "rating": return new RatingCriterion();
|
case "rating": return new RatingCriterion();
|
||||||
case "resolution": return new ResolutionCriterion();
|
case "resolution": return new ResolutionCriterion();
|
||||||
|
case "duration": return new DurationCriterion(type, type);
|
||||||
case "favorite": return new FavoriteCriterion();
|
case "favorite": return new FavoriteCriterion();
|
||||||
case "hasMarkers": return new HasMarkersCriterion();
|
case "hasMarkers": return new HasMarkersCriterion();
|
||||||
case "isMissing": return new IsMissingCriterion();
|
case "isMissing": return new IsMissingCriterion();
|
||||||
@@ -25,7 +26,7 @@ export function makeCriteria(type: CriterionType = "none") {
|
|||||||
case "performers": return new PerformersCriterion();
|
case "performers": return new PerformersCriterion();
|
||||||
case "studios": return new StudiosCriterion();
|
case "studios": return new StudiosCriterion();
|
||||||
|
|
||||||
case "birth_year":
|
case "birth_year": return new NumberCriterion(type, type);
|
||||||
case "age":
|
case "age":
|
||||||
var ret = new NumberCriterion(type, type);
|
var ret = new NumberCriterion(type, type);
|
||||||
// null/not null doesn't make sense for these criteria
|
// null/not null doesn't make sense for these criteria
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
SceneMarkerFilterType,
|
SceneMarkerFilterType,
|
||||||
SortDirectionEnum,
|
SortDirectionEnum,
|
||||||
} from "../../core/generated-graphql";
|
} from "../../core/generated-graphql";
|
||||||
import { Criterion, ICriterionOption, CriterionType, CriterionOption, NumberCriterion, StringCriterion } from "./criteria/criterion";
|
import { Criterion, ICriterionOption, CriterionType, CriterionOption, NumberCriterion, StringCriterion, DurationCriterion } from "./criteria/criterion";
|
||||||
import { FavoriteCriterion, FavoriteCriterionOption } from "./criteria/favorite";
|
import { FavoriteCriterion, FavoriteCriterionOption } from "./criteria/favorite";
|
||||||
import { HasMarkersCriterion, HasMarkersCriterionOption } from "./criteria/has-markers";
|
import { HasMarkersCriterion, HasMarkersCriterionOption } from "./criteria/has-markers";
|
||||||
import { IsMissingCriterion, IsMissingCriterionOption } from "./criteria/is-missing";
|
import { IsMissingCriterion, IsMissingCriterionOption } from "./criteria/is-missing";
|
||||||
@@ -47,6 +47,10 @@ export class ListFilterModel {
|
|||||||
public criteria: Array<Criterion<any, any>> = [];
|
public criteria: Array<Criterion<any, any>> = [];
|
||||||
public totalCount: number = 0;
|
public totalCount: number = 0;
|
||||||
|
|
||||||
|
private static createCriterionOption(criterion: CriterionType) {
|
||||||
|
return new CriterionOption(Criterion.getLabel(criterion), criterion);
|
||||||
|
}
|
||||||
|
|
||||||
public constructor(filterMode: FilterMode) {
|
public constructor(filterMode: FilterMode) {
|
||||||
switch (filterMode) {
|
switch (filterMode) {
|
||||||
case FilterMode.Scenes:
|
case FilterMode.Scenes:
|
||||||
@@ -61,6 +65,7 @@ export class ListFilterModel {
|
|||||||
new NoneCriterionOption(),
|
new NoneCriterionOption(),
|
||||||
new RatingCriterionOption(),
|
new RatingCriterionOption(),
|
||||||
new ResolutionCriterionOption(),
|
new ResolutionCriterionOption(),
|
||||||
|
ListFilterModel.createCriterionOption("duration"),
|
||||||
new HasMarkersCriterionOption(),
|
new HasMarkersCriterionOption(),
|
||||||
new IsMissingCriterionOption(),
|
new IsMissingCriterionOption(),
|
||||||
new TagsCriterionOption(),
|
new TagsCriterionOption(),
|
||||||
@@ -96,7 +101,7 @@ export class ListFilterModel {
|
|||||||
];
|
];
|
||||||
|
|
||||||
this.criterionOptions = this.criterionOptions.concat(numberCriteria.concat(stringCriteria).map((c) => {
|
this.criterionOptions = this.criterionOptions.concat(numberCriteria.concat(stringCriteria).map((c) => {
|
||||||
return new CriterionOption(Criterion.getLabel(c), c);
|
return ListFilterModel.createCriterionOption(c);
|
||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
case FilterMode.Studios:
|
case FilterMode.Studios:
|
||||||
@@ -234,6 +239,11 @@ export class ListFilterModel {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "duration": {
|
||||||
|
const durationCrit = criterion as DurationCriterion;
|
||||||
|
result.duration = { value: durationCrit.value, modifier: durationCrit.modifier }
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "hasMarkers":
|
case "hasMarkers":
|
||||||
result.has_markers = (criterion as HasMarkersCriterion).value;
|
result.has_markers = (criterion as HasMarkersCriterion).value;
|
||||||
break;
|
break;
|
||||||
|
|||||||
48
ui/v2/src/utils/duration.ts
Normal file
48
ui/v2/src/utils/duration.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { TextUtils } from "./text";
|
||||||
|
|
||||||
|
export class DurationUtils {
|
||||||
|
public static secondsToString(seconds : number) {
|
||||||
|
let ret = TextUtils.secondsToTimestamp(seconds);
|
||||||
|
|
||||||
|
if (ret.startsWith("00:")) {
|
||||||
|
ret = ret.substr(3);
|
||||||
|
|
||||||
|
if (ret.startsWith("0")) {
|
||||||
|
ret = ret.substr(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static stringToSeconds(v : string) {
|
||||||
|
if (!v) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let splits = v.split(":");
|
||||||
|
|
||||||
|
if (splits.length > 3) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let seconds = 0;
|
||||||
|
let factor = 1;
|
||||||
|
while(splits.length > 0) {
|
||||||
|
let thisSplit = splits.pop();
|
||||||
|
if (thisSplit == undefined) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let thisInt = parseInt(thisSplit, 10);
|
||||||
|
if (isNaN(thisInt)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
seconds += factor * thisInt;
|
||||||
|
factor *= 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
return seconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user